diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2015-12-30 11:46:15 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2015-12-30 11:46:15 +0000 |
| commit | dd58ef019b700900793a1eb48b52123db01b654e (patch) | |
| tree | fcfbb4df56a744f4ddc6122c50521dd3f1c5e196 /tools | |
| parent | 2fe5752e3a7c345cdb59e869278d36af33c13fa4 (diff) | |
Notes
Diffstat (limited to 'tools')
134 files changed, 9680 insertions, 4440 deletions
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index c9c5a1fdfa08..9b89d87fc571 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,78 +1,45 @@ -add_llvm_tool_subdirectory(llvm-config) +# This file will recurse into all subdirectories that contain CMakeLists.txt +# Setting variables that match the pattern LLVM_TOOL_{NAME}_BUILD to Off will +# prevent traversing into a directory. +# +# The only tools that need to be explicitly added are ones that have explicit +# ordering requirements. + +# Iterates all the subdirectories to create CMake options to enable/disable +# traversing each directory. +create_llvm_tool_options() # Build polly before the tools: the tools link against polly when # LINK_POLLY_INTO_TOOLS is set. if(WITH_POLLY) add_llvm_external_project(polly) -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) + set(LLVM_TOOL_POLLY_BUILD Off) endif() -add_llvm_tool_subdirectory(opt) -add_llvm_tool_subdirectory(llvm-as) -add_llvm_tool_subdirectory(llvm-dis) -add_llvm_tool_subdirectory(llvm-mc) - -add_llvm_tool_subdirectory(llc) -add_llvm_tool_subdirectory(llvm-ar) -add_llvm_tool_subdirectory(llvm-nm) -add_llvm_tool_subdirectory(llvm-size) - -add_llvm_tool_subdirectory(llvm-cov) -add_llvm_tool_subdirectory(llvm-profdata) -add_llvm_tool_subdirectory(llvm-link) -add_llvm_tool_subdirectory(lli) - -add_llvm_tool_subdirectory(llvm-extract) -add_llvm_tool_subdirectory(llvm-diff) -add_llvm_tool_subdirectory(macho-dump) -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(dsymutil) -add_llvm_tool_subdirectory(llvm-cxxdump) -if( LLVM_USE_INTEL_JITEVENTS ) - add_llvm_tool_subdirectory(llvm-jitlistener) -else() - ignore_llvm_tool_subdirectory(llvm-jitlistener) -endif( LLVM_USE_INTEL_JITEVENTS ) - -add_llvm_tool_subdirectory(bugpoint) -add_llvm_tool_subdirectory(bugpoint-passes) -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) - -add_llvm_tool_subdirectory(obj2yaml) -add_llvm_tool_subdirectory(yaml2obj) - -add_llvm_tool_subdirectory(llvm-go) +if(NOT LLVM_BUILD_LLVM_DYLIB ) + set(LLVM_TOOL_LLVM_SHLIB_BUILD Off) +endif() -add_llvm_tool_subdirectory(llvm-pdbdump) +if(NOT LLVM_USE_INTEL_JITEVENTS ) + set(LLVM_TOOL_LLVM_JITLISTENER_BUILD Off) +endif() -if(NOT CYGWIN AND LLVM_ENABLE_PIC) - add_llvm_tool_subdirectory(lto) - add_llvm_tool_subdirectory(llvm-lto) -else() - ignore_llvm_tool_subdirectory(lto) - ignore_llvm_tool_subdirectory(llvm-lto) +if(CYGWIN) + set(LLVM_TOOL_LTO_BUILD Off) + set(LLVM_TOOL_LLVM_LTO_BUILD Off) endif() -add_llvm_tool_subdirectory(gold) +# Add LTO, llvm-ar, llvm-config, and llvm-profdata before clang, ExternalProject +# requires targets specified in DEPENDS to exist before the call to +# ExternalProject_Add. +add_llvm_tool_subdirectory(lto) +add_llvm_tool_subdirectory(llvm-ar) +add_llvm_tool_subdirectory(llvm-config) +add_llvm_tool_subdirectory(llvm-profdata) +# Projects supported via LLVM_EXTERNAL_*_SOURCE_DIR need to be explicitly +# specified. add_llvm_external_project(clang) add_llvm_external_project(llgo) add_llvm_external_project(lld) @@ -80,6 +47,6 @@ add_llvm_external_project(lldb) # Automatically add remaining sub-directories containing a 'CMakeLists.txt' # file as external projects. -add_llvm_implicit_external_projects() +add_llvm_implicit_projects() set(LLVM_COMMON_DEPENDS ${LLVM_COMMON_DEPENDS} PARENT_SCOPE) diff --git a/tools/LLVMBuild.txt b/tools/LLVMBuild.txt index acf61b0268c1..d4b014771859 100644 --- a/tools/LLVMBuild.txt +++ b/tools/LLVMBuild.txt @@ -28,6 +28,7 @@ subdirectories = llvm-diff llvm-dis llvm-dwarfdump + llvm-dwp llvm-extract llvm-jitlistener llvm-link @@ -40,7 +41,7 @@ subdirectories = llvm-profdata llvm-rtdyld llvm-size - macho-dump + llvm-split opt verify-uselistorder diff --git a/tools/Makefile b/tools/Makefile index a47710f111a4..92d495451879 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -29,10 +29,11 @@ OPTIONAL_DIRS := lldb DIRS := llvm-config 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-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-cxxdump verify-uselistorder dsymutil llvm-pdbdump + llvm-cxxdump verify-uselistorder dsymutil llvm-pdbdump \ + llvm-split sancov llvm-dwp # If Intel JIT Events support is configured, build an extra tool to test it. ifeq ($(USE_INTEL_JITEVENTS), 1) diff --git a/tools/bugpoint-passes/TestPasses.cpp b/tools/bugpoint-passes/TestPasses.cpp index 6979d0345ee6..c1eb0fbb67c1 100644 --- a/tools/bugpoint-passes/TestPasses.cpp +++ b/tools/bugpoint-passes/TestPasses.cpp @@ -76,20 +76,21 @@ static RegisterPass<DeleteCalls> namespace { /// CrashOnDeclFunc - This pass is used to test bugpoint. It intentionally - /// crash if the module has an undefined function (ie a function that is - /// defined in an external module). - class CrashOnDeclFunc : public ModulePass { - public: - static char ID; // Pass ID, replacement for typeid - CrashOnDeclFunc() : ModulePass(ID) {} - private: - bool runOnModule(Module &M) override { - for (auto &F : M.functions()) { - if (F.isDeclaration()) - abort(); - } - return false; +/// crashes if the module has an undefined function (ie a function that is +/// defined in an external module). +class CrashOnDeclFunc : public ModulePass { +public: + static char ID; // Pass ID, replacement for typeid + CrashOnDeclFunc() : ModulePass(ID) {} + +private: + bool runOnModule(Module &M) override { + for (auto &F : M.functions()) { + if (F.isDeclaration()) + abort(); } + return false; + } }; } @@ -97,3 +98,29 @@ char CrashOnDeclFunc::ID = 0; static RegisterPass<CrashOnDeclFunc> Z("bugpoint-crash-decl-funcs", "BugPoint Test Pass - Intentionally crash on declared functions"); + +#include <iostream> +namespace { +/// CrashOnOneCU - This pass is used to test bugpoint. It intentionally +/// crashes if the Module has two or more compile units +class CrashOnTooManyCUs : public ModulePass { +public: + static char ID; + CrashOnTooManyCUs() : ModulePass(ID) {} + +private: + bool runOnModule(Module &M) override { + NamedMDNode *CU_Nodes = M.getNamedMetadata("llvm.dbg.cu"); + if (!CU_Nodes) + return false; + if (CU_Nodes->getNumOperands() >= 2) + abort(); + return false; + } +}; +} + +char CrashOnTooManyCUs::ID = 0; +static RegisterPass<CrashOnTooManyCUs> + A("bugpoint-crash-too-many-cus", + "BugPoint Test Pass - Intentionally crash on too many CUs"); diff --git a/tools/bugpoint/BugDriver.cpp b/tools/bugpoint/BugDriver.cpp index 43f4c2963fc3..030749fa9ea0 100644 --- a/tools/bugpoint/BugDriver.cpp +++ b/tools/bugpoint/BugDriver.cpp @@ -72,7 +72,7 @@ BugDriver::BugDriver(const char *toolname, bool find_bugs, LLVMContext& ctxt) : Context(ctxt), ToolName(toolname), ReferenceOutputFile(OutputFile), Program(nullptr), Interpreter(nullptr), SafeInterpreter(nullptr), - gcc(nullptr), run_find_bugs(find_bugs), Timeout(timeout), + cc(nullptr), run_find_bugs(find_bugs), Timeout(timeout), MemoryLimit(memlimit), UseValgrind(use_valgrind) {} BugDriver::~BugDriver() { @@ -80,7 +80,7 @@ BugDriver::~BugDriver() { if (Interpreter != SafeInterpreter) delete Interpreter; delete SafeInterpreter; - delete gcc; + delete cc; } std::unique_ptr<Module> llvm::parseInputFile(StringRef Filename, @@ -132,7 +132,7 @@ bool BugDriver::addSources(const std::vector<std::string> &Filenames) { if (!M.get()) return true; outs() << "Linking in input file: '" << Filenames[i] << "'\n"; - if (Linker::LinkModules(Program, M.get())) + if (Linker::linkModules(*Program, std::move(M))) return true; } diff --git a/tools/bugpoint/BugDriver.h b/tools/bugpoint/BugDriver.h index 579781246c54..20efff3fda5f 100644 --- a/tools/bugpoint/BugDriver.h +++ b/tools/bugpoint/BugDriver.h @@ -36,7 +36,7 @@ class LLVMContext; class DebugCrashes; -class GCC; +class CC; extern bool DisableSimplifyCFG; @@ -52,7 +52,7 @@ class BugDriver { std::vector<std::string> PassesToRun; AbstractInterpreter *Interpreter; // How to run the program AbstractInterpreter *SafeInterpreter; // To generate reference output, etc. - GCC *gcc; + CC *cc; bool run_find_bugs; unsigned Timeout; unsigned MemoryLimit; @@ -321,16 +321,21 @@ void PrintFunctionList(const std::vector<Function*> &Funcs); /// void PrintGlobalVariableList(const std::vector<GlobalVariable*> &GVs); +// DeleteGlobalInitializer - "Remove" the global variable by deleting its +// initializer, making it external. +// +void DeleteGlobalInitializer(GlobalVariable *GV); + // DeleteFunctionBody - "Remove" the function by deleting all of it's basic // blocks, making it external. // void DeleteFunctionBody(Function *F); -/// SplitFunctionsOutOfModule - Given a module and a list of functions in the -/// module, split the functions OUT of the specified module, and place them in -/// the new module. -Module *SplitFunctionsOutOfModule(Module *M, const std::vector<Function*> &F, - ValueToValueMapTy &VMap); +/// Given a module and a list of functions in the module, split the functions +/// OUT of the specified module, and place them in the new module. +std::unique_ptr<Module> +SplitFunctionsOutOfModule(Module *M, const std::vector<Function *> &F, + ValueToValueMapTy &VMap); } // End llvm namespace diff --git a/tools/bugpoint/CMakeLists.txt b/tools/bugpoint/CMakeLists.txt index daf502e16ccd..287a26ecd40c 100644 --- a/tools/bugpoint/CMakeLists.txt +++ b/tools/bugpoint/CMakeLists.txt @@ -3,7 +3,6 @@ set(LLVM_LINK_COMPONENTS BitWriter CodeGen Core - IPA IPO IRReader InstCombine diff --git a/tools/bugpoint/CrashDebugger.cpp b/tools/bugpoint/CrashDebugger.cpp index e2aaf6b01fc5..6cdc43ab8699 100644 --- a/tools/bugpoint/CrashDebugger.cpp +++ b/tools/bugpoint/CrashDebugger.cpp @@ -15,6 +15,7 @@ #include "ListReducer.h" #include "ToolRunner.h" #include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/StringSet.h" #include "llvm/IR/CFG.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DerivedTypes.h" @@ -49,6 +50,10 @@ namespace { DontReducePassList("disable-pass-list-reduction", cl::desc("Skip pass list reduction steps"), cl::init(false)); + + cl::opt<bool> NoNamedMDRM("disable-namedmd-remove", + cl::desc("Do not remove global named metadata"), + cl::init(false)); } namespace llvm { @@ -138,7 +143,7 @@ ReduceCrashingGlobalVariables::TestGlobalVariables( std::vector<GlobalVariable*> &GVs) { // Clone the program to try hacking it apart... ValueToValueMapTy VMap; - Module *M = CloneModule(BD.getProgram(), VMap); + Module *M = CloneModule(BD.getProgram(), VMap).release(); // Convert list to set for fast lookup... std::set<GlobalVariable*> GVSet; @@ -155,11 +160,10 @@ ReduceCrashingGlobalVariables::TestGlobalVariables( // Loop over and delete any global variables which we aren't supposed to be // playing with... - for (Module::global_iterator I = M->global_begin(), E = M->global_end(); - I != E; ++I) - if (I->hasInitializer() && !GVSet.count(I)) { - I->setInitializer(nullptr); - I->setLinkage(GlobalValue::ExternalLinkage); + for (GlobalVariable &I : M->globals()) + if (I.hasInitializer() && !GVSet.count(&I)) { + DeleteGlobalInitializer(&I); + I.setLinkage(GlobalValue::ExternalLinkage); } // Try running the hacked up program... @@ -235,7 +239,7 @@ bool ReduceCrashingFunctions::TestFuncs(std::vector<Function*> &Funcs) { // Clone the program to try hacking it apart... ValueToValueMapTy VMap; - Module *M = CloneModule(BD.getProgram(), VMap); + Module *M = CloneModule(BD.getProgram(), VMap).release(); // Convert list to set for fast lookup... std::set<Function*> Functions; @@ -253,9 +257,9 @@ bool ReduceCrashingFunctions::TestFuncs(std::vector<Function*> &Funcs) { if (!ReplaceFuncsWithNull) { // Loop over and delete any functions which we aren't supposed to be playing // with... - for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) - if (!I->isDeclaration() && !Functions.count(I)) - DeleteFunctionBody(I); + for (Function &I : *M) + if (!I.isDeclaration() && !Functions.count(&I)) + DeleteFunctionBody(&I); } else { std::vector<GlobalValue*> ToRemove; // First, remove aliases to functions we're about to purge. @@ -280,12 +284,12 @@ bool ReduceCrashingFunctions::TestFuncs(std::vector<Function*> &Funcs) { ToRemove.push_back(&Alias); } - for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) { - if (!I->isDeclaration() && !Functions.count(I)) { - PointerType *Ty = cast<PointerType>(I->getType()); + for (Function &I : *M) { + if (!I.isDeclaration() && !Functions.count(&I)) { + PointerType *Ty = cast<PointerType>(I.getType()); Constant *Replacement = ConstantPointerNull::get(Ty); - I->replaceAllUsesWith(Replacement); - ToRemove.push_back(I); + I.replaceAllUsesWith(Replacement); + ToRemove.push_back(&I); } } @@ -342,7 +346,7 @@ namespace { bool ReduceCrashingBlocks::TestBlocks(std::vector<const BasicBlock*> &BBs) { // Clone the program to try hacking it apart... ValueToValueMapTy VMap; - Module *M = CloneModule(BD.getProgram(), VMap); + Module *M = CloneModule(BD.getProgram(), VMap).release(); // Convert list to set for fast lookup... SmallPtrSet<BasicBlock*, 8> Blocks; @@ -361,20 +365,22 @@ bool ReduceCrashingBlocks::TestBlocks(std::vector<const BasicBlock*> &BBs) { // Loop over and delete any hack up any blocks that are not listed... for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) for (Function::iterator BB = I->begin(), E = I->end(); BB != E; ++BB) - if (!Blocks.count(BB) && BB->getTerminator()->getNumSuccessors()) { + if (!Blocks.count(&*BB) && BB->getTerminator()->getNumSuccessors()) { // Loop over all of the successors of this block, deleting any PHI nodes // that might include it. - for (succ_iterator SI = succ_begin(BB), E = succ_end(BB); SI != E; ++SI) - (*SI)->removePredecessor(BB); + for (succ_iterator SI = succ_begin(&*BB), E = succ_end(&*BB); SI != E; + ++SI) + (*SI)->removePredecessor(&*BB); TerminatorInst *BBTerm = BB->getTerminator(); - - if (!BB->getTerminator()->getType()->isVoidTy()) + if (BBTerm->isEHPad()) + continue; + if (!BBTerm->getType()->isVoidTy() && !BBTerm->getType()->isTokenTy()) BBTerm->replaceAllUsesWith(Constant::getNullValue(BBTerm->getType())); // Replace the old terminator instruction. BB->getInstList().pop_back(); - new UnreachableInst(BB->getContext(), BB); + new UnreachableInst(BB->getContext(), &*BB); } // The CFG Simplifier pass may delete one of the basic blocks we are @@ -450,7 +456,7 @@ bool ReduceCrashingInstructions::TestInsts(std::vector<const Instruction*> &Insts) { // Clone the program to try hacking it apart... ValueToValueMapTy VMap; - Module *M = CloneModule(BD.getProgram(), VMap); + Module *M = CloneModule(BD.getProgram(), VMap).release(); // Convert list to set for fast lookup... SmallPtrSet<Instruction*, 64> Instructions; @@ -468,10 +474,10 @@ bool ReduceCrashingInstructions::TestInsts(std::vector<const Instruction*> for (Module::iterator MI = M->begin(), ME = M->end(); MI != ME; ++MI) for (Function::iterator FI = MI->begin(), FE = MI->end(); FI != FE; ++FI) for (BasicBlock::iterator I = FI->begin(), E = FI->end(); I != E;) { - Instruction *Inst = I++; + Instruction *Inst = &*I++; if (!Instructions.count(Inst) && !isa<TerminatorInst>(Inst) && - !isa<LandingPadInst>(Inst)) { - if (!Inst->getType()->isVoidTy()) + !Inst->isEHPad()) { + if (!Inst->getType()->isVoidTy() && !Inst->getType()->isTokenTy()) Inst->replaceAllUsesWith(UndefValue::get(Inst->getType())); Inst->eraseFromParent(); } @@ -497,6 +503,149 @@ bool ReduceCrashingInstructions::TestInsts(std::vector<const Instruction*> return false; } +namespace { +// Reduce the list of Named Metadata nodes. We keep this as a list of +// names to avoid having to convert back and forth every time. +class ReduceCrashingNamedMD : public ListReducer<std::string> { + BugDriver &BD; + bool (*TestFn)(const BugDriver &, Module *); + +public: + ReduceCrashingNamedMD(BugDriver &bd, + bool (*testFn)(const BugDriver &, Module *)) + : BD(bd), TestFn(testFn) {} + + TestResult doTest(std::vector<std::string> &Prefix, + std::vector<std::string> &Kept, + std::string &Error) override { + if (!Kept.empty() && TestNamedMDs(Kept)) + return KeepSuffix; + if (!Prefix.empty() && TestNamedMDs(Prefix)) + return KeepPrefix; + return NoFailure; + } + + bool TestNamedMDs(std::vector<std::string> &NamedMDs); +}; +} + +bool ReduceCrashingNamedMD::TestNamedMDs(std::vector<std::string> &NamedMDs) { + + ValueToValueMapTy VMap; + Module *M = CloneModule(BD.getProgram(), VMap).release(); + + outs() << "Checking for crash with only these named metadata nodes:"; + unsigned NumPrint = std::min<size_t>(NamedMDs.size(), 10); + for (unsigned i = 0, e = NumPrint; i != e; ++i) + outs() << " " << NamedMDs[i]; + if (NumPrint < NamedMDs.size()) + outs() << "... <" << NamedMDs.size() << " total>"; + outs() << ": "; + + // Make a StringMap for faster lookup + StringSet<> Names; + for (const std::string &Name : NamedMDs) + Names.insert(Name); + + // First collect all the metadata to delete in a vector, then + // delete them all at once to avoid invalidating the iterator + std::vector<NamedMDNode *> ToDelete; + ToDelete.reserve(M->named_metadata_size() - Names.size()); + for (auto &NamedMD : M->named_metadata()) + if (!Names.count(NamedMD.getName())) + ToDelete.push_back(&NamedMD); + + for (auto *NamedMD : ToDelete) + NamedMD->eraseFromParent(); + + // Verify that this is still valid. + legacy::PassManager Passes; + Passes.add(createVerifierPass()); + Passes.run(*M); + + // Try running on the hacked up program... + if (TestFn(BD, M)) { + BD.setNewProgram(M); // It crashed, keep the trimmed version... + return true; + } + delete M; // It didn't crash, try something else. + return false; +} + +namespace { +// Reduce the list of operands to named metadata nodes +class ReduceCrashingNamedMDOps : public ListReducer<const MDNode *> { + BugDriver &BD; + bool (*TestFn)(const BugDriver &, Module *); + +public: + ReduceCrashingNamedMDOps(BugDriver &bd, + bool (*testFn)(const BugDriver &, Module *)) + : BD(bd), TestFn(testFn) {} + + TestResult doTest(std::vector<const MDNode *> &Prefix, + std::vector<const MDNode *> &Kept, + std::string &Error) override { + if (!Kept.empty() && TestNamedMDOps(Kept)) + return KeepSuffix; + if (!Prefix.empty() && TestNamedMDOps(Prefix)) + return KeepPrefix; + return NoFailure; + } + + bool TestNamedMDOps(std::vector<const MDNode *> &NamedMDOps); +}; +} + +bool ReduceCrashingNamedMDOps::TestNamedMDOps( + std::vector<const MDNode *> &NamedMDOps) { + // Convert list to set for fast lookup... + SmallPtrSet<const MDNode *, 64> OldMDNodeOps; + for (unsigned i = 0, e = NamedMDOps.size(); i != e; ++i) { + OldMDNodeOps.insert(NamedMDOps[i]); + } + + outs() << "Checking for crash with only " << OldMDNodeOps.size(); + if (OldMDNodeOps.size() == 1) + outs() << " named metadata operand: "; + else + outs() << " named metadata operands: "; + + ValueToValueMapTy VMap; + Module *M = CloneModule(BD.getProgram(), VMap).release(); + + // This is a little wasteful. In the future it might be good if we could have + // these dropped during cloning. + for (auto &NamedMD : BD.getProgram()->named_metadata()) { + // Drop the old one and create a new one + M->eraseNamedMetadata(M->getNamedMetadata(NamedMD.getName())); + NamedMDNode *NewNamedMDNode = + M->getOrInsertNamedMetadata(NamedMD.getName()); + for (MDNode *op : NamedMD.operands()) + if (OldMDNodeOps.count(op)) + NewNamedMDNode->addOperand(cast<MDNode>(MapMetadata(op, VMap))); + } + + // Verify that this is still valid. + legacy::PassManager Passes; + Passes.add(createVerifierPass()); + Passes.run(*M); + + // Try running on the hacked up program... + if (TestFn(BD, M)) { + // Make sure to use instruction pointers that point into the now-current + // module, and that they don't include any deleted blocks. + NamedMDOps.clear(); + for (const MDNode *Node : OldMDNodeOps) + NamedMDOps.push_back(cast<MDNode>(VMap.MD()[Node].get())); + + BD.setNewProgram(M); // It crashed, keep the trimmed version... + return true; + } + delete M; // It didn't crash, try something else. + return false; +} + /// DebugACrash - Given a predicate that determines whether a component crashes /// on a program, try to destructively reduce the program while still keeping /// the predicate true. @@ -509,13 +658,13 @@ static bool DebugACrash(BugDriver &BD, BD.getProgram()->global_begin() != BD.getProgram()->global_end()) { // Now try to reduce the number of global variable initializers in the // module to something small. - Module *M = CloneModule(BD.getProgram()); + Module *M = CloneModule(BD.getProgram()).release(); bool DeletedInit = false; for (Module::global_iterator I = M->global_begin(), E = M->global_end(); I != E; ++I) if (I->hasInitializer()) { - I->setInitializer(nullptr); + DeleteGlobalInitializer(&*I); I->setLinkage(GlobalValue::ExternalLinkage); DeletedInit = true; } @@ -538,7 +687,7 @@ static bool DebugACrash(BugDriver &BD, for (Module::global_iterator I = BD.getProgram()->global_begin(), E = BD.getProgram()->global_end(); I != E; ++I) if (I->hasInitializer()) - GVs.push_back(I); + GVs.push_back(&*I); if (GVs.size() > 1 && !BugpointIsInterrupted) { outs() << "\n*** Attempting to reduce the number of global " @@ -558,10 +707,9 @@ static bool DebugACrash(BugDriver &BD, // Now try to reduce the number of functions in the module to something small. std::vector<Function*> Functions; - for (Module::iterator I = BD.getProgram()->begin(), - E = BD.getProgram()->end(); I != E; ++I) - if (!I->isDeclaration()) - Functions.push_back(I); + for (Function &F : *BD.getProgram()) + if (!F.isDeclaration()) + Functions.push_back(&F); if (Functions.size() > 1 && !BugpointIsInterrupted) { outs() << "\n*** Attempting to reduce the number of functions " @@ -581,10 +729,9 @@ static bool DebugACrash(BugDriver &BD, // if (!DisableSimplifyCFG && !BugpointIsInterrupted) { std::vector<const BasicBlock*> Blocks; - for (Module::const_iterator I = BD.getProgram()->begin(), - E = BD.getProgram()->end(); I != E; ++I) - for (Function::const_iterator FI = I->begin(), E = I->end(); FI !=E; ++FI) - Blocks.push_back(FI); + for (Function &F : *BD.getProgram()) + for (BasicBlock &BB : F) + Blocks.push_back(&BB); unsigned OldSize = Blocks.size(); ReduceCrashingBlocks(BD, TestFn).reduceList(Blocks, Error); if (Blocks.size() < OldSize) @@ -595,14 +742,11 @@ static bool DebugACrash(BugDriver &BD, // cases with large basic blocks where the problem is at one end. if (!BugpointIsInterrupted) { std::vector<const Instruction*> Insts; - for (Module::const_iterator MI = BD.getProgram()->begin(), - ME = BD.getProgram()->end(); MI != ME; ++MI) - for (Function::const_iterator FI = MI->begin(), FE = MI->end(); FI != FE; - ++FI) - for (BasicBlock::const_iterator I = FI->begin(), E = FI->end(); - I != E; ++I) - if (!isa<TerminatorInst>(I)) - Insts.push_back(I); + for (const Function &F : *BD.getProgram()) + for (const BasicBlock &BB : F) + for (const Instruction &I : BB) + if (!isa<TerminatorInst>(&I)) + Insts.push_back(&I); ReduceCrashingInstructions(BD, TestFn).reduceList(Insts, Error); } @@ -642,12 +786,12 @@ static bool DebugACrash(BugDriver &BD, } else { if (BugpointIsInterrupted) goto ExitLoops; - if (isa<LandingPadInst>(I)) + if (I->isEHPad() || I->getType()->isTokenTy()) continue; outs() << "Checking instruction: " << *I; std::unique_ptr<Module> M = - BD.deleteInstructionFromProgram(I, Simplification); + BD.deleteInstructionFromProgram(&*I, Simplification); // Find out if the pass still crashes on this pass... if (TestFn(BD, M.get())) { @@ -666,12 +810,37 @@ static bool DebugACrash(BugDriver &BD, } } while (Simplification); + + if (!NoNamedMDRM) { + BD.EmitProgressBitcode(BD.getProgram(), "reduced-instructions"); + + if (!BugpointIsInterrupted) { + // Try to reduce the amount of global metadata (particularly debug info), + // by dropping global named metadata that anchors them + outs() << "\n*** Attempting to remove named metadata: "; + std::vector<std::string> NamedMDNames; + for (auto &NamedMD : BD.getProgram()->named_metadata()) + NamedMDNames.push_back(NamedMD.getName().str()); + ReduceCrashingNamedMD(BD, TestFn).reduceList(NamedMDNames, Error); + } + + if (!BugpointIsInterrupted) { + // Now that we quickly dropped all the named metadata that doesn't + // contribute to the crash, bisect the operands of the remaining ones + std::vector<const MDNode *> NamedMDOps; + for (auto &NamedMD : BD.getProgram()->named_metadata()) + for (auto op : NamedMD.operands()) + NamedMDOps.push_back(op); + ReduceCrashingNamedMDOps(BD, TestFn).reduceList(NamedMDOps, Error); + } + } + ExitLoops: // Try to clean up the testcase by running funcresolve and globaldce... if (!BugpointIsInterrupted) { outs() << "\n*** Attempting to perform final cleanups: "; - Module *M = CloneModule(BD.getProgram()); + Module *M = CloneModule(BD.getProgram()).release(); M = BD.performFinalCleanups(M, true).release(); // Find out if the pass still crashes on the cleaned up program... diff --git a/tools/bugpoint/ExecutionDriver.cpp b/tools/bugpoint/ExecutionDriver.cpp index 25813b34e62d..41b8ccc18e87 100644 --- a/tools/bugpoint/ExecutionDriver.cpp +++ b/tools/bugpoint/ExecutionDriver.cpp @@ -17,6 +17,7 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/FileUtilities.h" +#include "llvm/Support/Program.h" #include "llvm/Support/SystemUtils.h" #include "llvm/Support/raw_ostream.h" #include <fstream> @@ -124,11 +125,10 @@ namespace { cl::ZeroOrMore, cl::PositionalEatsArgs); cl::opt<std::string> - GCCBinary("gcc", cl::init("gcc"), - cl::desc("The gcc binary to use. (default 'gcc')")); + CCBinary("gcc", cl::init(""), cl::desc("The gcc binary to use.")); cl::list<std::string> - GCCToolArgv("gcc-tool-args", cl::Positional, + CCToolArgv("gcc-tool-args", cl::Positional, cl::desc("<gcc-tool arguments>..."), cl::ZeroOrMore, cl::PositionalEatsArgs); } @@ -148,6 +148,13 @@ bool BugDriver::initializeExecutionEnvironment() { SafeInterpreter = nullptr; std::string Message; + if (CCBinary.empty()) { + if (sys::findProgramByName("clang")) + CCBinary = "clang"; + else + CCBinary = "gcc"; + } + switch (InterpreterSel) { case AutoPick: if (!Interpreter) { @@ -158,8 +165,8 @@ bool BugDriver::initializeExecutionEnvironment() { if (!Interpreter) { InterpreterSel = RunLLC; Interpreter = AbstractInterpreter::createLLC(getToolName(), Message, - GCCBinary, &ToolArgv, - &GCCToolArgv); + CCBinary, &ToolArgv, + &CCToolArgv); } if (!Interpreter) { InterpreterSel = RunLLI; @@ -179,8 +186,8 @@ bool BugDriver::initializeExecutionEnvironment() { case RunLLCIA: case LLC_Safe: Interpreter = AbstractInterpreter::createLLC(getToolName(), Message, - GCCBinary, &ToolArgv, - &GCCToolArgv, + CCBinary, &ToolArgv, + &CCToolArgv, InterpreterSel == RunLLCIA); break; case RunJIT: @@ -213,9 +220,9 @@ bool BugDriver::initializeExecutionEnvironment() { SafeInterpreterSel = RunLLC; SafeToolArgs.push_back("--relocation-model=pic"); SafeInterpreter = AbstractInterpreter::createLLC(Path.c_str(), Message, - GCCBinary, + CCBinary, &SafeToolArgs, - &GCCToolArgv); + &CCToolArgv); } if (!SafeInterpreter && @@ -224,9 +231,9 @@ bool BugDriver::initializeExecutionEnvironment() { SafeInterpreterSel = RunLLC; SafeToolArgs.push_back("--relocation-model=pic"); SafeInterpreter = AbstractInterpreter::createLLC(Path.c_str(), Message, - GCCBinary, + CCBinary, &SafeToolArgs, - &GCCToolArgv); + &CCToolArgv); } if (!SafeInterpreter) { SafeInterpreterSel = AutoPick; @@ -237,8 +244,8 @@ bool BugDriver::initializeExecutionEnvironment() { case RunLLCIA: SafeToolArgs.push_back("--relocation-model=pic"); SafeInterpreter = AbstractInterpreter::createLLC(Path.c_str(), Message, - GCCBinary, &SafeToolArgs, - &GCCToolArgv, + CCBinary, &SafeToolArgs, + &CCToolArgv, SafeInterpreterSel == RunLLCIA); break; case Custom: @@ -252,8 +259,8 @@ bool BugDriver::initializeExecutionEnvironment() { } if (!SafeInterpreter) { outs() << Message << "\nExiting.\n"; exit(1); } - gcc = GCC::create(Message, GCCBinary, &GCCToolArgv); - if (!gcc) { outs() << Message << "\nExiting.\n"; exit(1); } + cc = CC::create(Message, CCBinary, &CCToolArgv); + if (!cc) { outs() << Message << "\nExiting.\n"; exit(1); } // If there was an error creating the selected interpreter, quit with error. return Interpreter == nullptr; @@ -388,13 +395,13 @@ std::string BugDriver::compileSharedObject(const std::string &BitcodeFile, std::string OutputFile; // Using the known-good backend. - GCC::FileType FT = SafeInterpreter->OutputCode(BitcodeFile, OutputFile, + CC::FileType FT = SafeInterpreter->OutputCode(BitcodeFile, OutputFile, Error); if (!Error.empty()) return ""; std::string SharedObjectFile; - bool Failure = gcc->MakeSharedObject(OutputFile, FT, SharedObjectFile, + bool Failure = cc->MakeSharedObject(OutputFile, FT, SharedObjectFile, AdditionalLinkerArgs, Error); if (!Error.empty()) return ""; diff --git a/tools/bugpoint/ExtractFunction.cpp b/tools/bugpoint/ExtractFunction.cpp index 238cbbc70a03..fe0ab69dc162 100644 --- a/tools/bugpoint/ExtractFunction.cpp +++ b/tools/bugpoint/ExtractFunction.cpp @@ -86,7 +86,7 @@ std::unique_ptr<Module> BugDriver::deleteInstructionFromProgram(const Instruction *I, unsigned Simplification) { // FIXME, use vmap? - Module *Clone = CloneModule(Program); + Module *Clone = CloneModule(Program).release(); const BasicBlock *PBB = I->getParent(); const Function *PF = PBB->getParent(); @@ -100,7 +100,7 @@ BugDriver::deleteInstructionFromProgram(const Instruction *I, BasicBlock::iterator RI = RBI->begin(); // Get iterator to corresponding inst std::advance(RI, std::distance(PBB->begin(), BasicBlock::const_iterator(I))); - Instruction *TheInst = RI; // Got the corresponding instruction! + Instruction *TheInst = &*RI; // Got the corresponding instruction! // If this instruction produces a value, replace any users with null values if (!TheInst->getType()->isVoidTy()) @@ -179,11 +179,43 @@ std::unique_ptr<Module> BugDriver::extractLoop(Module *M) { return NewM; } +static void eliminateAliases(GlobalValue *GV) { + // First, check whether a GlobalAlias references this definition. + // GlobalAlias MAY NOT reference declarations. + for (;;) { + // 1. Find aliases + SmallVector<GlobalAlias*,1> aliases; + Module *M = GV->getParent(); + for (Module::alias_iterator I=M->alias_begin(), E=M->alias_end(); I!=E; ++I) + if (I->getAliasee()->stripPointerCasts() == GV) + aliases.push_back(&*I); + if (aliases.empty()) + break; + // 2. Resolve aliases + for (unsigned i=0, e=aliases.size(); i<e; ++i) { + aliases[i]->replaceAllUsesWith(aliases[i]->getAliasee()); + aliases[i]->eraseFromParent(); + } + // 3. Repeat until no more aliases found; there might + // be an alias to an alias... + } +} + +// +// DeleteGlobalInitializer - "Remove" the global variable by deleting its initializer, +// making it external. +// +void llvm::DeleteGlobalInitializer(GlobalVariable *GV) { + eliminateAliases(GV); + GV->setInitializer(nullptr); +} // DeleteFunctionBody - "Remove" the function by deleting all of its basic // blocks, making it external. // void llvm::DeleteFunctionBody(Function *F) { + eliminateAliases(F); + // delete the body of the function... F->deleteBody(); assert(F->isDeclaration() && "This didn't make the function external!"); @@ -271,13 +303,8 @@ static void SplitStaticCtorDtor(const char *GlobalName, Module *M1, Module *M2, } } - -/// SplitFunctionsOutOfModule - Given a module and a list of functions in the -/// module, split the functions OUT of the specified module, and place them in -/// the new module. -Module * -llvm::SplitFunctionsOutOfModule(Module *M, - const std::vector<Function*> &F, +std::unique_ptr<Module> +llvm::SplitFunctionsOutOfModule(Module *M, const std::vector<Function *> &F, ValueToValueMapTy &VMap) { // Make sure functions & globals are all external so that linkage // between the two modules will work. @@ -291,7 +318,7 @@ llvm::SplitFunctionsOutOfModule(Module *M, } ValueToValueMapTy NewVMap; - Module *New = CloneModule(M, NewVMap); + std::unique_ptr<Module> New = CloneModule(M, NewVMap); // Remove the Test functions from the Safe module std::set<Function *> TestFunctions; @@ -306,16 +333,14 @@ llvm::SplitFunctionsOutOfModule(Module *M, // Remove the Safe functions from the Test module - for (Module::iterator I = New->begin(), E = New->end(); I != E; ++I) - if (!TestFunctions.count(I)) - DeleteFunctionBody(I); - + for (Function &I : *New) + if (!TestFunctions.count(&I)) + DeleteFunctionBody(&I); // Try to split the global initializers evenly - for (Module::global_iterator I = M->global_begin(), E = M->global_end(); - I != E; ++I) { - GlobalVariable *GV = cast<GlobalVariable>(NewVMap[I]); - if (Function *TestFn = globalInitUsesExternalBA(I)) { + for (GlobalVariable &I : M->globals()) { + GlobalVariable *GV = cast<GlobalVariable>(NewVMap[&I]); + if (Function *TestFn = globalInitUsesExternalBA(&I)) { if (Function *SafeFn = globalInitUsesExternalBA(GV)) { errs() << "*** Error: when reducing functions, encountered " "the global '"; @@ -325,18 +350,18 @@ llvm::SplitFunctionsOutOfModule(Module *M, << "' and from test function '" << TestFn->getName() << "'.\n"; exit(1); } - I->setInitializer(nullptr); // Delete the initializer to make it external + DeleteGlobalInitializer(&I); // Delete the initializer to make it external } else { // If we keep it in the safe module, then delete it in the test module - GV->setInitializer(nullptr); + DeleteGlobalInitializer(GV); } } // Make sure that there is a global ctor/dtor array in both halves of the // module if they both have static ctor/dtor functions. - SplitStaticCtorDtor("llvm.global_ctors", M, New, NewVMap); - SplitStaticCtorDtor("llvm.global_dtors", M, New, NewVMap); - + SplitStaticCtorDtor("llvm.global_ctors", M, New.get(), NewVMap); + SplitStaticCtorDtor("llvm.global_dtors", M, New.get(), NewVMap); + return New; } diff --git a/tools/bugpoint/ListReducer.h b/tools/bugpoint/ListReducer.h index a0bb570a5cb2..f08bc97a9988 100644 --- a/tools/bugpoint/ListReducer.h +++ b/tools/bugpoint/ListReducer.h @@ -75,6 +75,11 @@ struct ListReducer { // Maximal number of allowed splitting iterations, // before the elements are randomly shuffled. const unsigned MaxIterationsWithoutProgress = 3; + + // Maximal number of allowed single-element trim iterations. We add a + // threshhold here as single-element reductions may otherwise take a + // very long time to complete. + const unsigned MaxTrimIterationsWithoutBackJump = 3; bool ShufflingEnabled = true; Backjump: @@ -157,6 +162,7 @@ Backjump: if (TheList.size() > 2) { bool Changed = true; std::vector<ElTy> EmptyList; + unsigned TrimIterations = 0; while (Changed) { // Trimming loop. Changed = false; @@ -186,9 +192,9 @@ Backjump: if (!Error.empty()) return true; } - // This can take a long time if left uncontrolled. For now, don't - // iterate. - break; + if (TrimIterations >= MaxTrimIterationsWithoutBackJump) + break; + TrimIterations++; } } diff --git a/tools/bugpoint/Miscompilation.cpp b/tools/bugpoint/Miscompilation.cpp index fad16368698d..16919f5a4492 100644 --- a/tools/bugpoint/Miscompilation.cpp +++ b/tools/bugpoint/Miscompilation.cpp @@ -176,12 +176,15 @@ ReduceMiscompilingPasses::doTest(std::vector<std::string> &Prefix, namespace { class ReduceMiscompilingFunctions : public ListReducer<Function*> { BugDriver &BD; - bool (*TestFn)(BugDriver &, Module *, Module *, std::string &); + bool (*TestFn)(BugDriver &, std::unique_ptr<Module>, + std::unique_ptr<Module>, std::string &); + public: ReduceMiscompilingFunctions(BugDriver &bd, - bool (*F)(BugDriver &, Module *, Module *, + bool (*F)(BugDriver &, std::unique_ptr<Module>, + std::unique_ptr<Module>, std::string &)) - : BD(bd), TestFn(F) {} + : BD(bd), TestFn(F) {} TestResult doTest(std::vector<Function*> &Prefix, std::vector<Function*> &Suffix, @@ -207,32 +210,24 @@ namespace { }; } -/// TestMergedProgram - Given two modules, link them together and run the -/// program, checking to see if the program matches the diff. If there is -/// an error, return NULL. If not, return the merged module. The Broken argument -/// will be set to true if the output is different. If the DeleteInputs -/// argument is set to true then this function deletes both input -/// modules before it returns. +/// Given two modules, link them together and run the program, checking to see +/// if the program matches the diff. If there is an error, return NULL. If not, +/// return the merged module. The Broken argument will be set to true if the +/// output is different. If the DeleteInputs argument is set to true then this +/// function deletes both input modules before it returns. /// -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. - if (!DeleteInputs) { - M1 = CloneModule(M1); - M2 = CloneModule(M2); - } - if (Linker::LinkModules(M1, M2)) +static std::unique_ptr<Module> testMergedProgram(const BugDriver &BD, + std::unique_ptr<Module> M1, + std::unique_ptr<Module> M2, + std::string &Error, + bool &Broken) { + if (Linker::linkModules(*M1, std::move(M2))) exit(1); - delete M2; // We are done with this module. // Execute the program. - Broken = BD.diffProgram(M1, "", "", false, &Error); - if (!Error.empty()) { - // Delete the linked module - delete M1; + Broken = BD.diffProgram(M1.get(), "", "", false, &Error); + if (!Error.empty()) return nullptr; - } return M1; } @@ -259,7 +254,7 @@ bool ReduceMiscompilingFunctions::TestFuncs(const std::vector<Function*> &Funcs, // we can conclude that a function triggers the bug when in fact one // needs a larger set of original functions to do so. ValueToValueMapTy VMap; - Module *Clone = CloneModule(BD.getProgram(), VMap); + Module *Clone = CloneModule(BD.getProgram(), VMap).release(); Module *Orig = BD.swapProgramIn(Clone); std::vector<Function*> FuncsOnClone; @@ -270,12 +265,12 @@ bool ReduceMiscompilingFunctions::TestFuncs(const std::vector<Function*> &Funcs, // Split the module into the two halves of the program we want. VMap.clear(); - Module *ToNotOptimize = CloneModule(BD.getProgram(), VMap); - Module *ToOptimize = SplitFunctionsOutOfModule(ToNotOptimize, FuncsOnClone, - VMap); + std::unique_ptr<Module> ToNotOptimize = CloneModule(BD.getProgram(), VMap); + std::unique_ptr<Module> ToOptimize = + SplitFunctionsOutOfModule(ToNotOptimize.get(), FuncsOnClone, VMap); - // Run the predicate, note that the predicate will delete both input modules. - bool Broken = TestFn(BD, ToOptimize, ToNotOptimize, Error); + bool Broken = + TestFn(BD, std::move(ToOptimize), std::move(ToNotOptimize), Error); delete BD.swapProgramIn(Orig); @@ -294,29 +289,29 @@ static void DisambiguateGlobalSymbols(Module *M) { I->setName("anon_fn"); } -/// ExtractLoops - Given a reduced list of functions that still exposed the bug, -/// check to see if we can extract the loops in the region without obscuring the -/// bug. If so, it reduces the amount of code identified. +/// Given a reduced list of functions that still exposed the bug, check to see +/// if we can extract the loops in the region without obscuring the bug. If so, +/// it reduces the amount of code identified. /// static bool ExtractLoops(BugDriver &BD, - bool (*TestFn)(BugDriver &, Module *, Module *, - std::string &), - std::vector<Function*> &MiscompiledFunctions, + bool (*TestFn)(BugDriver &, std::unique_ptr<Module>, + std::unique_ptr<Module>, std::string &), + std::vector<Function *> &MiscompiledFunctions, std::string &Error) { bool MadeChange = false; while (1) { if (BugpointIsInterrupted) return MadeChange; ValueToValueMapTy VMap; - Module *ToNotOptimize = CloneModule(BD.getProgram(), VMap); - Module *ToOptimize = SplitFunctionsOutOfModule(ToNotOptimize, - MiscompiledFunctions, - VMap); - Module *ToOptimizeLoopExtracted = BD.extractLoop(ToOptimize).release(); + std::unique_ptr<Module> ToNotOptimize = CloneModule(BD.getProgram(), VMap); + Module *ToOptimize = SplitFunctionsOutOfModule(ToNotOptimize.get(), + MiscompiledFunctions, VMap) + .release(); + std::unique_ptr<Module> ToOptimizeLoopExtracted = + BD.extractLoop(ToOptimize); if (!ToOptimizeLoopExtracted) { // If the loop extractor crashed or if there were no extractible loops, // then this chapter of our odyssey is over with. - delete ToNotOptimize; delete ToOptimize; return MadeChange; } @@ -330,13 +325,14 @@ static bool ExtractLoops(BugDriver &BD, // extraction. AbstractInterpreter *AI = BD.switchToSafeInterpreter(); bool Failure; - Module *New = TestMergedProgram(BD, ToOptimizeLoopExtracted, - ToNotOptimize, false, Error, Failure); + std::unique_ptr<Module> New = + testMergedProgram(BD, std::move(ToOptimizeLoopExtracted), + std::move(ToNotOptimize), Error, Failure); if (!New) return false; // Delete the original and set the new program. - Module *Old = BD.swapProgramIn(New); + Module *Old = BD.swapProgramIn(New.release()); for (unsigned i = 0, e = MiscompiledFunctions.size(); i != e; ++i) MiscompiledFunctions[i] = cast<Function>(VMap[MiscompiledFunctions[i]]); delete Old; @@ -350,16 +346,15 @@ static bool ExtractLoops(BugDriver &BD, errs() << " Continuing on with un-loop-extracted version.\n"; BD.writeProgramToFile(OutputPrefix + "-loop-extract-fail-tno.bc", - ToNotOptimize); + ToNotOptimize.get()); BD.writeProgramToFile(OutputPrefix + "-loop-extract-fail-to.bc", ToOptimize); BD.writeProgramToFile(OutputPrefix + "-loop-extract-fail-to-le.bc", - ToOptimizeLoopExtracted); + ToOptimizeLoopExtracted.get()); errs() << "Please submit the " << OutputPrefix << "-loop-extract-fail-*.bc files.\n"; delete ToOptimize; - delete ToNotOptimize; return MadeChange; } delete ToOptimize; @@ -367,18 +362,20 @@ static bool ExtractLoops(BugDriver &BD, outs() << " Testing after loop extraction:\n"; // Clone modules, the tester function will free them. - Module *TOLEBackup = CloneModule(ToOptimizeLoopExtracted, VMap); - Module *TNOBackup = CloneModule(ToNotOptimize, VMap); + std::unique_ptr<Module> TOLEBackup = + CloneModule(ToOptimizeLoopExtracted.get(), VMap); + std::unique_ptr<Module> TNOBackup = CloneModule(ToNotOptimize.get(), VMap); for (unsigned i = 0, e = MiscompiledFunctions.size(); i != e; ++i) MiscompiledFunctions[i] = cast<Function>(VMap[MiscompiledFunctions[i]]); - Failure = TestFn(BD, ToOptimizeLoopExtracted, ToNotOptimize, Error); + Failure = TestFn(BD, std::move(ToOptimizeLoopExtracted), + std::move(ToNotOptimize), Error); if (!Error.empty()) return false; - ToOptimizeLoopExtracted = TOLEBackup; - ToNotOptimize = TNOBackup; + ToOptimizeLoopExtracted = std::move(TOLEBackup); + ToNotOptimize = std::move(TNOBackup); if (!Failure) { outs() << "*** Loop extraction masked the problem. Undoing.\n"; @@ -390,7 +387,8 @@ static bool ExtractLoops(BugDriver &BD, MisCompFunctions.emplace_back(F->getName(), F->getFunctionType()); } - if (Linker::LinkModules(ToNotOptimize, ToOptimizeLoopExtracted)) + if (Linker::linkModules(*ToNotOptimize, + std::move(ToOptimizeLoopExtracted))) exit(1); MiscompiledFunctions.clear(); @@ -401,8 +399,7 @@ static bool ExtractLoops(BugDriver &BD, MiscompiledFunctions.push_back(NewF); } - delete ToOptimizeLoopExtracted; - BD.setNewProgram(ToNotOptimize); + BD.setNewProgram(ToNotOptimize.release()); return MadeChange; } @@ -418,11 +415,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. - if (Linker::LinkModules(ToNotOptimize, ToOptimizeLoopExtracted)) + if (Linker::linkModules(*ToNotOptimize, std::move(ToOptimizeLoopExtracted))) exit(1); - delete ToOptimizeLoopExtracted; - // All of the Function*'s in the MiscompiledFunctions list are in the old // module. Update this list to include all of the functions in the // optimized and loop extracted module. @@ -434,7 +429,7 @@ static bool ExtractLoops(BugDriver &BD, MiscompiledFunctions.push_back(NewF); } - BD.setNewProgram(ToNotOptimize); + BD.setNewProgram(ToNotOptimize.release()); MadeChange = true; } } @@ -442,14 +437,15 @@ static bool ExtractLoops(BugDriver &BD, namespace { class ReduceMiscompiledBlocks : public ListReducer<BasicBlock*> { BugDriver &BD; - bool (*TestFn)(BugDriver &, Module *, Module *, std::string &); + bool (*TestFn)(BugDriver &, std::unique_ptr<Module>, + std::unique_ptr<Module>, std::string &); std::vector<Function*> FunctionsBeingTested; public: ReduceMiscompiledBlocks(BugDriver &bd, - bool (*F)(BugDriver &, Module *, Module *, - std::string &), - const std::vector<Function*> &Fns) - : BD(bd), TestFn(F), FunctionsBeingTested(Fns) {} + bool (*F)(BugDriver &, std::unique_ptr<Module>, + std::unique_ptr<Module>, std::string &), + const std::vector<Function *> &Fns) + : BD(bd), TestFn(F), FunctionsBeingTested(Fns) {} TestResult doTest(std::vector<BasicBlock*> &Prefix, std::vector<BasicBlock*> &Suffix, @@ -495,7 +491,7 @@ bool ReduceMiscompiledBlocks::TestFuncs(const std::vector<BasicBlock*> &BBs, // Split the module into the two halves of the program we want. ValueToValueMapTy VMap; - Module *Clone = CloneModule(BD.getProgram(), VMap); + Module *Clone = CloneModule(BD.getProgram(), VMap).release(); Module *Orig = BD.swapProgramIn(Clone); std::vector<Function*> FuncsOnClone; std::vector<BasicBlock*> BBsOnClone; @@ -509,45 +505,37 @@ bool ReduceMiscompiledBlocks::TestFuncs(const std::vector<BasicBlock*> &BBs, } VMap.clear(); - Module *ToNotOptimize = CloneModule(BD.getProgram(), VMap); - Module *ToOptimize = SplitFunctionsOutOfModule(ToNotOptimize, - FuncsOnClone, - VMap); + std::unique_ptr<Module> ToNotOptimize = CloneModule(BD.getProgram(), VMap); + std::unique_ptr<Module> ToOptimize = + SplitFunctionsOutOfModule(ToNotOptimize.get(), FuncsOnClone, VMap); // 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 (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.get(), ToNotOptimize, Error); + BD.extractMappedBlocksFromModule(BBsOnClone, ToOptimize.get())) { + bool Ret = TestFn(BD, std::move(New), std::move(ToNotOptimize), Error); delete BD.swapProgramIn(Orig); return Ret; } delete BD.swapProgramIn(Orig); - delete ToOptimize; - delete ToNotOptimize; return false; } - -/// ExtractBlocks - Given a reduced list of functions that still expose the bug, -/// extract as many basic blocks from the region as possible without obscuring -/// the bug. +/// Given a reduced list of functions that still expose the bug, extract as many +/// basic blocks from the region as possible without obscuring the bug. /// static bool ExtractBlocks(BugDriver &BD, - bool (*TestFn)(BugDriver &, Module *, Module *, + bool (*TestFn)(BugDriver &, std::unique_ptr<Module>, + std::unique_ptr<Module>, std::string &), - std::vector<Function*> &MiscompiledFunctions, + std::vector<Function *> &MiscompiledFunctions, std::string &Error) { if (BugpointIsInterrupted) return false; std::vector<BasicBlock*> Blocks; for (unsigned i = 0, e = MiscompiledFunctions.size(); i != e; ++i) - for (Function::iterator I = MiscompiledFunctions[i]->begin(), - E = MiscompiledFunctions[i]->end(); I != E; ++I) - Blocks.push_back(I); + for (BasicBlock &BB : *MiscompiledFunctions[i]) + Blocks.push_back(&BB); // Use the list reducer to identify blocks that can be extracted without // obscuring the bug. The Blocks list will end up containing blocks that must @@ -571,10 +559,10 @@ static bool ExtractBlocks(BugDriver &BD, } ValueToValueMapTy VMap; - Module *ProgClone = CloneModule(BD.getProgram(), VMap); - Module *ToExtract = SplitFunctionsOutOfModule(ProgClone, - MiscompiledFunctions, - VMap); + Module *ProgClone = CloneModule(BD.getProgram(), VMap).release(); + Module *ToExtract = + SplitFunctionsOutOfModule(ProgClone, MiscompiledFunctions, VMap) + .release(); std::unique_ptr<Module> Extracted = BD.extractMappedBlocksFromModule(Blocks, ToExtract); if (!Extracted) { @@ -595,7 +583,7 @@ static bool ExtractBlocks(BugDriver &BD, if (!I->isDeclaration()) MisCompFunctions.emplace_back(I->getName(), I->getFunctionType()); - if (Linker::LinkModules(ProgClone, Extracted.get())) + if (Linker::linkModules(*ProgClone, std::move(Extracted))) exit(1); // Set the new program and delete the old one. @@ -613,14 +601,13 @@ static bool ExtractBlocks(BugDriver &BD, return true; } - -/// DebugAMiscompilation - This is a generic driver to narrow down -/// miscompilations, either in an optimization or a code generator. +/// This is a generic driver to narrow down miscompilations, either in an +/// optimization or a code generator. /// -static std::vector<Function*> +static std::vector<Function *> DebugAMiscompilation(BugDriver &BD, - bool (*TestFn)(BugDriver &, Module *, Module *, - std::string &), + bool (*TestFn)(BugDriver &, std::unique_ptr<Module>, + std::unique_ptr<Module>, std::string &), std::string &Error) { // Okay, now that we have reduced the list of passes which are causing the // failure, see if we can pin down which functions are being @@ -628,9 +615,9 @@ DebugAMiscompilation(BugDriver &BD, // the program. std::vector<Function*> MiscompiledFunctions; Module *Prog = BD.getProgram(); - for (Module::iterator I = Prog->begin(), E = Prog->end(); I != E; ++I) - if (!I->isDeclaration()) - MiscompiledFunctions.push_back(I); + for (Function &F : *Prog) + if (!F.isDeclaration()) + MiscompiledFunctions.push_back(&F); // Do the reduction... if (!BugpointIsInterrupted) @@ -699,28 +686,28 @@ DebugAMiscompilation(BugDriver &BD, return MiscompiledFunctions; } -/// TestOptimizer - This is the predicate function used to check to see if the -/// "Test" portion of the program is misoptimized. If so, return true. In any -/// case, both module arguments are deleted. +/// This is the predicate function used to check to see if the "Test" portion of +/// the program is misoptimized. If so, return true. In any case, both module +/// arguments are deleted. /// -static bool TestOptimizer(BugDriver &BD, Module *Test, Module *Safe, - std::string &Error) { +static bool TestOptimizer(BugDriver &BD, std::unique_ptr<Module> Test, + std::unique_ptr<Module> Safe, std::string &Error) { // Run the optimization passes on ToOptimize, producing a transformed version // of the functions being tested. outs() << " Optimizing functions being tested: "; - std::unique_ptr<Module> Optimized = BD.runPassesOn(Test, BD.getPassesToRun(), - /*AutoDebugCrashes*/ true); + std::unique_ptr<Module> Optimized = + BD.runPassesOn(Test.get(), 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.get(), Safe, true, Error, Broken); + std::unique_ptr<Module> New = testMergedProgram( + BD, std::move(Optimized), std::move(Safe), Error, Broken); if (New) { outs() << (Broken ? " nope.\n" : " yup.\n"); // Delete the original and set the new program. - delete BD.swapProgramIn(New); + delete BD.swapProgramIn(New.release()); } return Broken; } @@ -753,10 +740,10 @@ void BugDriver::debugMiscompilation(std::string *Error) { // Output a bunch of bitcode files for the user... outs() << "Outputting reduced bitcode files which expose the problem:\n"; ValueToValueMapTy VMap; - Module *ToNotOptimize = CloneModule(getProgram(), VMap); - Module *ToOptimize = SplitFunctionsOutOfModule(ToNotOptimize, - MiscompiledFunctions, - VMap); + Module *ToNotOptimize = CloneModule(getProgram(), VMap).release(); + Module *ToOptimize = + SplitFunctionsOutOfModule(ToNotOptimize, MiscompiledFunctions, VMap) + .release(); outs() << " Non-optimized portion: "; EmitProgressBitcode(ToNotOptimize, "tonotoptimize", true); @@ -769,13 +756,13 @@ void BugDriver::debugMiscompilation(std::string *Error) { return; } -/// CleanupAndPrepareModules - Get the specified modules ready for code -/// generator testing. +/// Get the specified modules ready for code generator testing. /// -static void CleanupAndPrepareModules(BugDriver &BD, Module *&Test, +static void CleanupAndPrepareModules(BugDriver &BD, + std::unique_ptr<Module> &Test, Module *Safe) { // Clean up the modules, removing extra cruft that we don't need anymore... - Test = BD.performFinalCleanups(Test).release(); + Test = BD.performFinalCleanups(Test.get()); // If we are executing the JIT, we have several nasty issues to take care of. if (!BD.isExecutingJIT()) return; @@ -788,21 +775,21 @@ static void CleanupAndPrepareModules(BugDriver &BD, Module *&Test, // Rename it oldMain->setName("llvm_bugpoint_old_main"); // Create a NEW `main' function with same type in the test module. - Function *newMain = Function::Create(oldMain->getFunctionType(), - GlobalValue::ExternalLinkage, - "main", Test); + Function *newMain = + Function::Create(oldMain->getFunctionType(), + GlobalValue::ExternalLinkage, "main", Test.get()); // Create an `oldmain' prototype in the test module, which will // corresponds to the real main function in the same module. Function *oldMainProto = Function::Create(oldMain->getFunctionType(), GlobalValue::ExternalLinkage, - oldMain->getName(), Test); + oldMain->getName(), Test.get()); // Set up and remember the argument list for the main function. std::vector<Value*> args; for (Function::arg_iterator I = newMain->arg_begin(), E = newMain->arg_end(), OI = oldMain->arg_begin(); I != E; ++I, ++OI) { I->setName(OI->getName()); // Copy argument names from oldMain - args.push_back(I); + args.push_back(&*I); } // Call the old main function and return its result @@ -905,9 +892,8 @@ static void CleanupAndPrepareModules(BugDriver &BD, Module *&Test, // Save the argument list. std::vector<Value*> Args; - for (Function::arg_iterator i = FuncWrapper->arg_begin(), - e = FuncWrapper->arg_end(); i != e; ++i) - Args.push_back(i); + for (Argument &A : FuncWrapper->args()) + Args.push_back(&A); // Pass on the arguments to the real function, return its result if (F->getReturnType()->isVoidTy()) { @@ -932,15 +918,14 @@ static void CleanupAndPrepareModules(BugDriver &BD, Module *&Test, } } - - -/// TestCodeGenerator - This is the predicate function used to check to see if -/// the "Test" portion of the program is miscompiled by the code generator under -/// test. If so, return true. In any case, both module arguments are deleted. +/// This is the predicate function used to check to see if the "Test" portion of +/// the program is miscompiled by the code generator under test. If so, return +/// true. In any case, both module arguments are deleted. /// -static bool TestCodeGenerator(BugDriver &BD, Module *Test, Module *Safe, +static bool TestCodeGenerator(BugDriver &BD, std::unique_ptr<Module> Test, + std::unique_ptr<Module> Safe, std::string &Error) { - CleanupAndPrepareModules(BD, Test, Safe); + CleanupAndPrepareModules(BD, Test, Safe.get()); SmallString<128> TestModuleBC; int TestModuleFD; @@ -951,12 +936,11 @@ static bool TestCodeGenerator(BugDriver &BD, Module *Test, Module *Safe, << EC.message() << "\n"; exit(1); } - if (BD.writeProgramToFile(TestModuleBC.str(), TestModuleFD, Test)) { + if (BD.writeProgramToFile(TestModuleBC.str(), TestModuleFD, Test.get())) { errs() << "Error writing bitcode to `" << TestModuleBC.str() << "'\nExiting."; exit(1); } - delete Test; FileRemover TestModuleBCRemover(TestModuleBC.str(), !SaveTemps); @@ -971,7 +955,7 @@ static bool TestCodeGenerator(BugDriver &BD, Module *Test, Module *Safe, exit(1); } - if (BD.writeProgramToFile(SafeModuleBC.str(), SafeModuleFD, Safe)) { + if (BD.writeProgramToFile(SafeModuleBC.str(), SafeModuleFD, Safe.get())) { errs() << "Error writing bitcode to `" << SafeModuleBC << "'\nExiting."; exit(1); @@ -982,7 +966,6 @@ static bool TestCodeGenerator(BugDriver &BD, Module *Test, Module *Safe, std::string SharedObject = BD.compileSharedObject(SafeModuleBC.str(), Error); if (!Error.empty()) return false; - delete Safe; FileRemover SharedObjectRemover(SharedObject, !SaveTemps); @@ -1030,11 +1013,12 @@ bool BugDriver::debugCodeGenerator(std::string *Error) { // Split the module into the two halves of the program we want. ValueToValueMapTy VMap; - Module *ToNotCodeGen = CloneModule(getProgram(), VMap); - Module *ToCodeGen = SplitFunctionsOutOfModule(ToNotCodeGen, Funcs, VMap); + std::unique_ptr<Module> ToNotCodeGen = CloneModule(getProgram(), VMap); + std::unique_ptr<Module> ToCodeGen = + SplitFunctionsOutOfModule(ToNotCodeGen.get(), Funcs, VMap); // Condition the modules - CleanupAndPrepareModules(*this, ToCodeGen, ToNotCodeGen); + CleanupAndPrepareModules(*this, ToCodeGen, ToNotCodeGen.get()); SmallString<128> TestModuleBC; int TestModuleFD; @@ -1046,12 +1030,11 @@ bool BugDriver::debugCodeGenerator(std::string *Error) { exit(1); } - if (writeProgramToFile(TestModuleBC.str(), TestModuleFD, ToCodeGen)) { + if (writeProgramToFile(TestModuleBC.str(), TestModuleFD, ToCodeGen.get())) { errs() << "Error writing bitcode to `" << TestModuleBC << "'\nExiting."; exit(1); } - delete ToCodeGen; // Make the shared library SmallString<128> SafeModuleBC; @@ -1064,7 +1047,8 @@ bool BugDriver::debugCodeGenerator(std::string *Error) { exit(1); } - if (writeProgramToFile(SafeModuleBC.str(), SafeModuleFD, ToNotCodeGen)) { + if (writeProgramToFile(SafeModuleBC.str(), SafeModuleFD, + ToNotCodeGen.get())) { errs() << "Error writing bitcode to `" << SafeModuleBC << "'\nExiting."; exit(1); @@ -1072,7 +1056,6 @@ bool BugDriver::debugCodeGenerator(std::string *Error) { std::string SharedObject = compileSharedObject(SafeModuleBC.str(), *Error); if (!Error->empty()) return true; - delete ToNotCodeGen; outs() << "You can reproduce the problem with the command line: \n"; if (isExecutingJIT()) { @@ -1080,7 +1063,7 @@ bool BugDriver::debugCodeGenerator(std::string *Error) { } else { outs() << " llc " << TestModuleBC << " -o " << TestModuleBC << ".s\n"; - outs() << " gcc " << SharedObject << " " << TestModuleBC.str() + outs() << " cc " << SharedObject << " " << TestModuleBC.str() << ".s -o " << TestModuleBC << ".exe"; #if defined (HAVE_LINK_R) outs() << " -Wl,-R."; @@ -1093,7 +1076,7 @@ bool BugDriver::debugCodeGenerator(std::string *Error) { outs() << '\n'; outs() << "The shared object was created with:\n llc -march=c " << SafeModuleBC.str() << " -o temporary.c\n" - << " gcc -xc temporary.c -O2 -o " << SharedObject; + << " cc -xc temporary.c -O2 -o " << SharedObject; if (TargetTriple.getArch() == Triple::sparc) outs() << " -G"; // Compile a shared library, `-G' for Sparc else diff --git a/tools/bugpoint/ToolRunner.cpp b/tools/bugpoint/ToolRunner.cpp index 51091e297d7d..2ccd64905128 100644 --- a/tools/bugpoint/ToolRunner.cpp +++ b/tools/bugpoint/ToolRunner.cpp @@ -64,16 +64,6 @@ static int RunProgramWithTimeout(StringRef ProgramPath, unsigned MemoryLimit = 0, std::string *ErrMsg = nullptr) { const StringRef *Redirects[3] = { &StdInFile, &StdOutFile, &StdErrFile }; - -#if 0 // For debug purposes - { - errs() << "RUN:"; - for (unsigned i = 0; Args[i]; ++i) - errs() << " " << Args[i]; - errs() << "\n"; - } -#endif - return sys::ExecuteAndWait(ProgramPath, Args, nullptr, Redirects, NumSeconds, MemoryLimit, ErrMsg); } @@ -93,15 +83,6 @@ static int RunProgramRemotelyWithTimeout(StringRef RemoteClientPath, unsigned MemoryLimit = 0) { const StringRef *Redirects[3] = { &StdInFile, &StdOutFile, &StdErrFile }; -#if 0 // For debug purposes - { - errs() << "RUN:"; - for (unsigned i = 0; Args[i]; ++i) - errs() << " " << Args[i]; - errs() << "\n"; - } -#endif - // Run the program remotely with the remote client int ReturnCode = sys::ExecuteAndWait(RemoteClientPath, Args, nullptr, Redirects, NumSeconds, MemoryLimit); @@ -152,7 +133,7 @@ static std::string ProcessFailure(StringRef ProgPath, const char** Args, ErrorFilename.str(), Timeout, MemoryLimit); // FIXME: check return code ? - // Print out the error messages generated by GCC if possible... + // Print out the error messages generated by CC if possible... std::ifstream ErrorFile(ErrorFilename.c_str()); if (ErrorFile) { std::copy(std::istreambuf_iterator<char>(ErrorFile), @@ -184,7 +165,7 @@ namespace { const std::string &InputFile, const std::string &OutputFile, std::string *Error, - const std::vector<std::string> &GCCArgs, + const std::vector<std::string> &CCArgs, const std::vector<std::string> &SharedLibs = std::vector<std::string>(), unsigned Timeout = 0, @@ -197,7 +178,7 @@ int LLI::ExecuteProgram(const std::string &Bitcode, const std::string &InputFile, const std::string &OutputFile, std::string *Error, - const std::vector<std::string> &GCCArgs, + const std::vector<std::string> &CCArgs, const std::vector<std::string> &SharedLibs, unsigned Timeout, unsigned MemoryLimit) { @@ -305,7 +286,7 @@ namespace { const std::string &InputFile, const std::string &OutputFile, std::string *Error, - const std::vector<std::string> &GCCArgs = + const std::vector<std::string> &CCArgs = std::vector<std::string>(), const std::vector<std::string> &SharedLibs = std::vector<std::string>(), @@ -361,7 +342,7 @@ namespace { const std::string &InputFile, const std::string &OutputFile, std::string *Error, - const std::vector<std::string> &GCCArgs, + const std::vector<std::string> &CCArgs, const std::vector<std::string> &SharedLibs = std::vector<std::string>(), unsigned Timeout = 0, @@ -374,7 +355,7 @@ int CustomExecutor::ExecuteProgram(const std::string &Bitcode, const std::string &InputFile, const std::string &OutputFile, std::string *Error, - const std::vector<std::string> &GCCArgs, + const std::vector<std::string> &CCArgs, const std::vector<std::string> &SharedLibs, unsigned Timeout, unsigned MemoryLimit) { @@ -473,7 +454,7 @@ AbstractInterpreter *AbstractInterpreter::createCustomExecutor( //===----------------------------------------------------------------------===// // LLC Implementation of AbstractIntepreter interface // -GCC::FileType LLC::OutputCode(const std::string &Bitcode, +CC::FileType LLC::OutputCode(const std::string &Bitcode, std::string &OutputAsmFile, std::string &Error, unsigned Timeout, unsigned MemoryLimit) { const char *Suffix = (UseIntegratedAssembler ? ".llc.o" : ".llc.s"); @@ -514,7 +495,7 @@ GCC::FileType LLC::OutputCode(const std::string &Bitcode, Timeout, MemoryLimit)) Error = ProcessFailure(LLCPath, &LLCArgs[0], Timeout, MemoryLimit); - return UseIntegratedAssembler ? GCC::ObjectFile : GCC::AsmFile; + return UseIntegratedAssembler ? CC::ObjectFile : CC::AsmFile; } void LLC::compileProgram(const std::string &Bitcode, std::string *Error, @@ -529,22 +510,22 @@ int LLC::ExecuteProgram(const std::string &Bitcode, const std::string &InputFile, const std::string &OutputFile, std::string *Error, - const std::vector<std::string> &ArgsForGCC, + const std::vector<std::string> &ArgsForCC, const std::vector<std::string> &SharedLibs, unsigned Timeout, unsigned MemoryLimit) { std::string OutputAsmFile; - GCC::FileType FileKind = OutputCode(Bitcode, OutputAsmFile, *Error, Timeout, + CC::FileType FileKind = OutputCode(Bitcode, OutputAsmFile, *Error, Timeout, MemoryLimit); FileRemover OutFileRemover(OutputAsmFile, !SaveTemps); - std::vector<std::string> GCCArgs(ArgsForGCC); - GCCArgs.insert(GCCArgs.end(), SharedLibs.begin(), SharedLibs.end()); + std::vector<std::string> CCArgs(ArgsForCC); + CCArgs.insert(CCArgs.end(), SharedLibs.begin(), SharedLibs.end()); - // Assuming LLC worked, compile the result with GCC and run it. - return gcc->ExecuteProgram(OutputAsmFile, Args, FileKind, - InputFile, OutputFile, Error, GCCArgs, + // Assuming LLC worked, compile the result with CC and run it. + return cc->ExecuteProgram(OutputAsmFile, Args, FileKind, + InputFile, OutputFile, Error, CCArgs, Timeout, MemoryLimit); } @@ -552,9 +533,9 @@ int LLC::ExecuteProgram(const std::string &Bitcode, /// LLC *AbstractInterpreter::createLLC(const char *Argv0, std::string &Message, - const std::string &GCCBinary, + const std::string &CCBinary, const std::vector<std::string> *Args, - const std::vector<std::string> *GCCArgs, + const std::vector<std::string> *CCArgs, bool UseIntegratedAssembler) { std::string LLCPath = PrependMainExecutablePath("llc", Argv0, (void *)(intptr_t) & createLLC); @@ -563,13 +544,13 @@ LLC *AbstractInterpreter::createLLC(const char *Argv0, return nullptr; } - GCC *gcc = GCC::create(Message, GCCBinary, GCCArgs); - if (!gcc) { + CC *cc = CC::create(Message, CCBinary, CCArgs); + if (!cc) { errs() << Message << "\n"; exit(1); } Message = "Found llc: " + LLCPath + "\n"; - return new LLC(LLCPath, gcc, Args, UseIntegratedAssembler); + return new LLC(LLCPath, cc, Args, UseIntegratedAssembler); } //===---------------------------------------------------------------------===// @@ -591,7 +572,7 @@ namespace { const std::string &InputFile, const std::string &OutputFile, std::string *Error, - const std::vector<std::string> &GCCArgs = + const std::vector<std::string> &CCArgs = std::vector<std::string>(), const std::vector<std::string> &SharedLibs = std::vector<std::string>(), @@ -605,7 +586,7 @@ int JIT::ExecuteProgram(const std::string &Bitcode, const std::string &InputFile, const std::string &OutputFile, std::string *Error, - const std::vector<std::string> &GCCArgs, + const std::vector<std::string> &CCArgs, const std::vector<std::string> &SharedLibs, unsigned Timeout, unsigned MemoryLimit) { @@ -656,7 +637,7 @@ AbstractInterpreter *AbstractInterpreter::createJIT(const char *Argv0, } //===---------------------------------------------------------------------===// -// GCC abstraction +// CC abstraction // static bool IsARMArchitecture(std::vector<const char*> Args) { @@ -672,82 +653,82 @@ static bool IsARMArchitecture(std::vector<const char*> Args) { return false; } -int GCC::ExecuteProgram(const std::string &ProgramFile, +int CC::ExecuteProgram(const std::string &ProgramFile, const std::vector<std::string> &Args, FileType fileType, const std::string &InputFile, const std::string &OutputFile, std::string *Error, - const std::vector<std::string> &ArgsForGCC, + const std::vector<std::string> &ArgsForCC, unsigned Timeout, unsigned MemoryLimit) { - std::vector<const char*> GCCArgs; + std::vector<const char*> CCArgs; - GCCArgs.push_back(GCCPath.c_str()); + CCArgs.push_back(CCPath.c_str()); if (TargetTriple.getArch() == Triple::x86) - GCCArgs.push_back("-m32"); + CCArgs.push_back("-m32"); for (std::vector<std::string>::const_iterator - I = gccArgs.begin(), E = gccArgs.end(); I != E; ++I) - GCCArgs.push_back(I->c_str()); + I = ccArgs.begin(), E = ccArgs.end(); I != E; ++I) + CCArgs.push_back(I->c_str()); // Specify -x explicitly in case the extension is wonky if (fileType != ObjectFile) { - GCCArgs.push_back("-x"); + CCArgs.push_back("-x"); if (fileType == CFile) { - GCCArgs.push_back("c"); - GCCArgs.push_back("-fno-strict-aliasing"); + CCArgs.push_back("c"); + CCArgs.push_back("-fno-strict-aliasing"); } else { - GCCArgs.push_back("assembler"); + CCArgs.push_back("assembler"); // For ARM architectures we don't want this flag. bugpoint isn't // explicitly told what architecture it is working on, so we get - // it from gcc flags - if (TargetTriple.isOSDarwin() && !IsARMArchitecture(GCCArgs)) - GCCArgs.push_back("-force_cpusubtype_ALL"); + // it from cc flags + if (TargetTriple.isOSDarwin() && !IsARMArchitecture(CCArgs)) + CCArgs.push_back("-force_cpusubtype_ALL"); } } - GCCArgs.push_back(ProgramFile.c_str()); // Specify the input filename. + CCArgs.push_back(ProgramFile.c_str()); // Specify the input filename. - GCCArgs.push_back("-x"); - GCCArgs.push_back("none"); - GCCArgs.push_back("-o"); + CCArgs.push_back("-x"); + CCArgs.push_back("none"); + CCArgs.push_back("-o"); SmallString<128> OutputBinary; std::error_code EC = - sys::fs::createUniqueFile(ProgramFile + "-%%%%%%%.gcc.exe", OutputBinary); + sys::fs::createUniqueFile(ProgramFile + "-%%%%%%%.cc.exe", OutputBinary); if (EC) { errs() << "Error making unique filename: " << EC.message() << "\n"; exit(1); } - GCCArgs.push_back(OutputBinary.c_str()); // Output to the right file... + CCArgs.push_back(OutputBinary.c_str()); // Output to the right file... - // Add any arguments intended for GCC. We locate them here because this is + // Add any arguments intended for CC. We locate them here because this is // most likely -L and -l options that need to come before other libraries but // after the source. Other options won't be sensitive to placement on the // command line, so this should be safe. - for (unsigned i = 0, e = ArgsForGCC.size(); i != e; ++i) - GCCArgs.push_back(ArgsForGCC[i].c_str()); + for (unsigned i = 0, e = ArgsForCC.size(); i != e; ++i) + CCArgs.push_back(ArgsForCC[i].c_str()); - GCCArgs.push_back("-lm"); // Hard-code the math library... - GCCArgs.push_back("-O2"); // Optimize the program a bit... + CCArgs.push_back("-lm"); // Hard-code the math library... + CCArgs.push_back("-O2"); // Optimize the program a bit... #if defined (HAVE_LINK_R) - GCCArgs.push_back("-Wl,-R."); // Search this dir for .so files + CCArgs.push_back("-Wl,-R."); // Search this dir for .so files #endif if (TargetTriple.getArch() == Triple::sparc) - GCCArgs.push_back("-mcpu=v9"); - GCCArgs.push_back(nullptr); // NULL terminator + CCArgs.push_back("-mcpu=v9"); + CCArgs.push_back(nullptr); // NULL terminator - outs() << "<gcc>"; outs().flush(); + outs() << "<CC>"; outs().flush(); DEBUG(errs() << "\nAbout to run:\t"; - for (unsigned i = 0, e = GCCArgs.size()-1; i != e; ++i) - errs() << " " << GCCArgs[i]; + for (unsigned i = 0, e = CCArgs.size()-1; i != e; ++i) + errs() << " " << CCArgs[i]; errs() << "\n"; ); - if (RunProgramWithTimeout(GCCPath, &GCCArgs[0], "", "", "")) { - *Error = ProcessFailure(GCCPath, &GCCArgs[0]); + if (RunProgramWithTimeout(CCPath, &CCArgs[0], "", "", "")) { + *Error = ProcessFailure(CCPath, &CCArgs[0]); return -1; } @@ -821,9 +802,9 @@ int GCC::ExecuteProgram(const std::string &ProgramFile, } } -int GCC::MakeSharedObject(const std::string &InputFile, FileType fileType, +int CC::MakeSharedObject(const std::string &InputFile, FileType fileType, std::string &OutputFile, - const std::vector<std::string> &ArgsForGCC, + const std::vector<std::string> &ArgsForCC, std::string &Error) { SmallString<128> UniqueFilename; std::error_code EC = sys::fs::createUniqueFile( @@ -834,84 +815,84 @@ int GCC::MakeSharedObject(const std::string &InputFile, FileType fileType, } OutputFile = UniqueFilename.str(); - std::vector<const char*> GCCArgs; + std::vector<const char*> CCArgs; - GCCArgs.push_back(GCCPath.c_str()); + CCArgs.push_back(CCPath.c_str()); if (TargetTriple.getArch() == Triple::x86) - GCCArgs.push_back("-m32"); + CCArgs.push_back("-m32"); for (std::vector<std::string>::const_iterator - I = gccArgs.begin(), E = gccArgs.end(); I != E; ++I) - GCCArgs.push_back(I->c_str()); + I = ccArgs.begin(), E = ccArgs.end(); I != E; ++I) + CCArgs.push_back(I->c_str()); // Compile the C/asm file into a shared object if (fileType != ObjectFile) { - GCCArgs.push_back("-x"); - GCCArgs.push_back(fileType == AsmFile ? "assembler" : "c"); + CCArgs.push_back("-x"); + CCArgs.push_back(fileType == AsmFile ? "assembler" : "c"); } - GCCArgs.push_back("-fno-strict-aliasing"); - GCCArgs.push_back(InputFile.c_str()); // Specify the input filename. - GCCArgs.push_back("-x"); - GCCArgs.push_back("none"); + CCArgs.push_back("-fno-strict-aliasing"); + CCArgs.push_back(InputFile.c_str()); // Specify the input filename. + CCArgs.push_back("-x"); + CCArgs.push_back("none"); if (TargetTriple.getArch() == Triple::sparc) - GCCArgs.push_back("-G"); // Compile a shared library, `-G' for Sparc + CCArgs.push_back("-G"); // Compile a shared library, `-G' for Sparc else if (TargetTriple.isOSDarwin()) { // link all source files into a single module in data segment, rather than // generating blocks. dynamic_lookup requires that you set // MACOSX_DEPLOYMENT_TARGET=10.3 in your env. FIXME: it would be better for - // bugpoint to just pass that in the environment of GCC. - GCCArgs.push_back("-single_module"); - GCCArgs.push_back("-dynamiclib"); // `-dynamiclib' for MacOS X/PowerPC - GCCArgs.push_back("-undefined"); - GCCArgs.push_back("dynamic_lookup"); + // bugpoint to just pass that in the environment of CC. + CCArgs.push_back("-single_module"); + CCArgs.push_back("-dynamiclib"); // `-dynamiclib' for MacOS X/PowerPC + CCArgs.push_back("-undefined"); + CCArgs.push_back("dynamic_lookup"); } else - GCCArgs.push_back("-shared"); // `-shared' for Linux/X86, maybe others + CCArgs.push_back("-shared"); // `-shared' for Linux/X86, maybe others if (TargetTriple.getArch() == Triple::x86_64) - GCCArgs.push_back("-fPIC"); // Requires shared objs to contain PIC + CCArgs.push_back("-fPIC"); // Requires shared objs to contain PIC if (TargetTriple.getArch() == Triple::sparc) - GCCArgs.push_back("-mcpu=v9"); + CCArgs.push_back("-mcpu=v9"); - GCCArgs.push_back("-o"); - GCCArgs.push_back(OutputFile.c_str()); // Output to the right filename. - GCCArgs.push_back("-O2"); // Optimize the program a bit. + CCArgs.push_back("-o"); + CCArgs.push_back(OutputFile.c_str()); // Output to the right filename. + CCArgs.push_back("-O2"); // Optimize the program a bit. - // Add any arguments intended for GCC. We locate them here because this is + // Add any arguments intended for CC. We locate them here because this is // most likely -L and -l options that need to come before other libraries but // after the source. Other options won't be sensitive to placement on the // command line, so this should be safe. - for (unsigned i = 0, e = ArgsForGCC.size(); i != e; ++i) - GCCArgs.push_back(ArgsForGCC[i].c_str()); - GCCArgs.push_back(nullptr); // NULL terminator + for (unsigned i = 0, e = ArgsForCC.size(); i != e; ++i) + CCArgs.push_back(ArgsForCC[i].c_str()); + CCArgs.push_back(nullptr); // NULL terminator - outs() << "<gcc>"; outs().flush(); + outs() << "<CC>"; outs().flush(); DEBUG(errs() << "\nAbout to run:\t"; - for (unsigned i = 0, e = GCCArgs.size()-1; i != e; ++i) - errs() << " " << GCCArgs[i]; + for (unsigned i = 0, e = CCArgs.size()-1; i != e; ++i) + errs() << " " << CCArgs[i]; errs() << "\n"; ); - if (RunProgramWithTimeout(GCCPath, &GCCArgs[0], "", "", "")) { - Error = ProcessFailure(GCCPath, &GCCArgs[0]); + if (RunProgramWithTimeout(CCPath, &CCArgs[0], "", "", "")) { + Error = ProcessFailure(CCPath, &CCArgs[0]); return 1; } return 0; } -/// create - Try to find the `gcc' executable +/// create - Try to find the CC executable /// -GCC *GCC::create(std::string &Message, - const std::string &GCCBinary, +CC *CC::create(std::string &Message, + const std::string &CCBinary, const std::vector<std::string> *Args) { - auto GCCPath = sys::findProgramByName(GCCBinary); - if (!GCCPath) { - Message = "Cannot find `" + GCCBinary + "' in PATH: " + - GCCPath.getError().message() + "\n"; + auto CCPath = sys::findProgramByName(CCBinary); + if (!CCPath) { + Message = "Cannot find `" + CCBinary + "' in PATH: " + + CCPath.getError().message() + "\n"; return nullptr; } @@ -926,6 +907,6 @@ GCC *GCC::create(std::string &Message, RemoteClientPath = *Path; } - Message = "Found gcc: " + *GCCPath + "\n"; - return new GCC(*GCCPath, RemoteClientPath, Args); + Message = "Found CC: " + *CCPath + "\n"; + return new CC(*CCPath, RemoteClientPath, Args); } diff --git a/tools/bugpoint/ToolRunner.h b/tools/bugpoint/ToolRunner.h index 5d67a9426870..3accd70ed4ce 100644 --- a/tools/bugpoint/ToolRunner.h +++ b/tools/bugpoint/ToolRunner.h @@ -33,22 +33,22 @@ extern Triple TargetTriple; class LLC; //===---------------------------------------------------------------------===// -// GCC abstraction +// CC abstraction // -class GCC { - std::string GCCPath; // The path to the gcc executable. +class CC { + std::string CCPath; // The path to the cc executable. std::string RemoteClientPath; // The path to the rsh / ssh executable. - std::vector<std::string> gccArgs; // GCC-specific arguments. - GCC(StringRef gccPath, StringRef RemotePath, - const std::vector<std::string> *GCCArgs) - : GCCPath(gccPath), RemoteClientPath(RemotePath) { - if (GCCArgs) gccArgs = *GCCArgs; + std::vector<std::string> ccArgs; // CC-specific arguments. + CC(StringRef ccPath, StringRef RemotePath, + const std::vector<std::string> *CCArgs) + : CCPath(ccPath), RemoteClientPath(RemotePath) { + if (CCArgs) ccArgs = *CCArgs; } public: enum FileType { AsmFile, ObjectFile, CFile }; - static GCC *create(std::string &Message, - const std::string &GCCBinary, + static CC *create(std::string &Message, + const std::string &CCBinary, const std::vector<std::string> *Args); /// ExecuteProgram - Execute the program specified by "ProgramFile" (which is @@ -64,7 +64,7 @@ public: const std::string &InputFile, const std::string &OutputFile, std::string *Error = nullptr, - const std::vector<std::string> &GCCArgs = + const std::vector<std::string> &CCArgs = std::vector<std::string>(), unsigned Timeout = 0, unsigned MemoryLimit = 0); @@ -74,7 +74,7 @@ public: /// int MakeSharedObject(const std::string &InputFile, FileType fileType, std::string &OutputFile, - const std::vector<std::string> &ArgsForGCC, + const std::vector<std::string> &ArgsForCC, std::string &Error); }; @@ -88,9 +88,9 @@ class AbstractInterpreter { virtual void anchor(); public: static LLC *createLLC(const char *Argv0, std::string &Message, - const std::string &GCCBinary, + const std::string &CCBinary, const std::vector<std::string> *Args = nullptr, - const std::vector<std::string> *GCCArgs = nullptr, + const std::vector<std::string> *CCArgs = nullptr, bool UseIntegratedAssembler = false); static AbstractInterpreter* @@ -119,15 +119,15 @@ public: unsigned Timeout = 0, unsigned MemoryLimit = 0) {} /// OutputCode - Compile the specified program from bitcode to code - /// understood by the GCC driver (either C or asm). If the code generator + /// understood by the CC driver (either C or asm). If the code generator /// fails, it sets Error, otherwise, this function returns the type of code /// emitted. - virtual GCC::FileType OutputCode(const std::string &Bitcode, + virtual CC::FileType OutputCode(const std::string &Bitcode, std::string &OutFile, std::string &Error, unsigned Timeout = 0, unsigned MemoryLimit = 0) { Error = "OutputCode not supported by this AbstractInterpreter!"; - return GCC::AsmFile; + return CC::AsmFile; } /// ExecuteProgram - Run the specified bitcode file, emitting output to the @@ -140,7 +140,7 @@ public: const std::string &InputFile, const std::string &OutputFile, std::string *Error, - const std::vector<std::string> &GCCArgs = + const std::vector<std::string> &CCArgs = std::vector<std::string>(), const std::vector<std::string> &SharedLibs = std::vector<std::string>(), @@ -154,18 +154,18 @@ public: class LLC : public AbstractInterpreter { std::string LLCPath; // The path to the LLC executable. std::vector<std::string> ToolArgs; // Extra args to pass to LLC. - GCC *gcc; + CC *cc; bool UseIntegratedAssembler; public: - LLC(const std::string &llcPath, GCC *Gcc, + LLC(const std::string &llcPath, CC *cc, const std::vector<std::string> *Args, bool useIntegratedAssembler) - : LLCPath(llcPath), gcc(Gcc), + : LLCPath(llcPath), cc(cc), UseIntegratedAssembler(useIntegratedAssembler) { ToolArgs.clear(); if (Args) ToolArgs = *Args; } - ~LLC() override { delete gcc; } + ~LLC() override { delete cc; } /// compileProgram - Compile the specified program from bitcode to executable /// code. This does not produce any output, it is only used when debugging @@ -178,7 +178,7 @@ public: const std::string &InputFile, const std::string &OutputFile, std::string *Error, - const std::vector<std::string> &GCCArgs = + const std::vector<std::string> &CCArgs = std::vector<std::string>(), const std::vector<std::string> &SharedLibs = std::vector<std::string>(), @@ -186,10 +186,10 @@ public: unsigned MemoryLimit = 0) override; /// OutputCode - Compile the specified program from bitcode to code - /// understood by the GCC driver (either C or asm). If the code generator + /// understood by the CC driver (either C or asm). If the code generator /// fails, it sets Error, otherwise, this function returns the type of code /// emitted. - GCC::FileType OutputCode(const std::string &Bitcode, + CC::FileType OutputCode(const std::string &Bitcode, std::string &OutFile, std::string &Error, unsigned Timeout = 0, unsigned MemoryLimit = 0) override; diff --git a/tools/bugpoint/bugpoint.cpp b/tools/bugpoint/bugpoint.cpp index af6d9fccf291..48f30e6709f8 100644 --- a/tools/bugpoint/bugpoint.cpp +++ b/tools/bugpoint/bugpoint.cpp @@ -126,7 +126,6 @@ int main(int argc, char **argv) { initializeVectorization(Registry); initializeIPO(Registry); initializeAnalysis(Registry); - initializeIPA(Registry); initializeTransformUtils(Registry); initializeInstCombine(Registry); initializeInstrumentation(Registry); @@ -181,19 +180,12 @@ int main(int argc, char **argv) { Builder.Inliner = createFunctionInliningPass(225); else Builder.Inliner = createFunctionInliningPass(275); - - // Note that although clang/llvm-gcc use two separate passmanagers - // here, it shouldn't normally make a difference. Builder.populateFunctionPassManager(PM); Builder.populateModulePassManager(PM); } - for (std::vector<const PassInfo*>::iterator I = PassList.begin(), - E = PassList.end(); - I != E; ++I) { - const PassInfo* PI = *I; + for (const PassInfo *PI : PassList) D.addPass(PI->getPassArgument()); - } // Bugpoint has the ability of generating a plethora of core files, so to // avoid filling up the disk, we prevent it diff --git a/tools/dsymutil/BinaryHolder.cpp b/tools/dsymutil/BinaryHolder.cpp index ad66105bc249..4c2c1d195c3c 100644 --- a/tools/dsymutil/BinaryHolder.cpp +++ b/tools/dsymutil/BinaryHolder.cpp @@ -13,25 +13,59 @@ //===----------------------------------------------------------------------===// #include "BinaryHolder.h" +#include "llvm/Object/MachO.h" #include "llvm/Support/raw_ostream.h" namespace llvm { namespace dsymutil { -ErrorOr<MemoryBufferRef> -BinaryHolder::GetMemoryBufferForFile(StringRef Filename) { +Triple BinaryHolder::getTriple(const object::MachOObjectFile &Obj) { + // If a ThumbTriple is returned, use it instead of the standard + // one. This is because the thumb triple always allows to create a + // target, whereas the non-thumb one might not. + Triple ThumbTriple; + Triple T = Obj.getArch(nullptr, &ThumbTriple); + return ThumbTriple.getArch() ? ThumbTriple : T; +} + +static std::vector<MemoryBufferRef> +getMachOFatMemoryBuffers(StringRef Filename, MemoryBuffer &Mem, + object::MachOUniversalBinary &Fat) { + std::vector<MemoryBufferRef> Buffers; + StringRef FatData = Fat.getData(); + for (auto It = Fat.begin_objects(), End = Fat.end_objects(); It != End; + ++It) { + StringRef ObjData = FatData.substr(It->getOffset(), It->getSize()); + Buffers.emplace_back(ObjData, Filename); + } + return Buffers; +} + +void BinaryHolder::changeBackingMemoryBuffer( + std::unique_ptr<MemoryBuffer> &&Buf) { + CurrentArchives.clear(); + CurrentObjectFiles.clear(); + CurrentFatBinary.reset(); + + CurrentMemoryBuffer = std::move(Buf); +} + +ErrorOr<std::vector<MemoryBufferRef>> +BinaryHolder::GetMemoryBuffersForFile(StringRef Filename, + sys::TimeValue Timestamp) { if (Verbose) outs() << "trying to open '" << Filename << "'\n"; // Try that first as it doesn't involve any filesystem access. - if (auto ErrOrArchiveMember = GetArchiveMemberBuffer(Filename)) - return *ErrOrArchiveMember; + if (auto ErrOrArchiveMembers = GetArchiveMemberBuffers(Filename, Timestamp)) + return *ErrOrArchiveMembers; // If the name ends with a closing paren, there is a huge chance // it is an archive member specification. if (Filename.endswith(")")) - if (auto ErrOrArchiveMember = MapArchiveAndGetMemberBuffer(Filename)) - return *ErrOrArchiveMember; + if (auto ErrOrArchiveMembers = + MapArchiveAndGetMemberBuffers(Filename, Timestamp)) + return *ErrOrArchiveMembers; // Otherwise, just try opening a standard file. If this is an // archive member specifiaction and any of the above didn't handle it @@ -42,39 +76,70 @@ BinaryHolder::GetMemoryBufferForFile(StringRef Filename) { if (auto Err = ErrOrFile.getError()) return Err; + changeBackingMemoryBuffer(std::move(*ErrOrFile)); if (Verbose) outs() << "\tloaded file.\n"; - CurrentArchive.reset(); - CurrentMemoryBuffer = std::move(ErrOrFile.get()); - return CurrentMemoryBuffer->getMemBufferRef(); + + auto ErrOrFat = object::MachOUniversalBinary::create( + CurrentMemoryBuffer->getMemBufferRef()); + if (ErrOrFat.getError()) { + // Not a fat binary must be a standard one. Return a one element vector. + return std::vector<MemoryBufferRef>{CurrentMemoryBuffer->getMemBufferRef()}; + } + + CurrentFatBinary = std::move(*ErrOrFat); + return getMachOFatMemoryBuffers(Filename, *CurrentMemoryBuffer, + *CurrentFatBinary); } -ErrorOr<MemoryBufferRef> -BinaryHolder::GetArchiveMemberBuffer(StringRef Filename) { - if (!CurrentArchive) +ErrorOr<std::vector<MemoryBufferRef>> +BinaryHolder::GetArchiveMemberBuffers(StringRef Filename, + sys::TimeValue Timestamp) { + if (CurrentArchives.empty()) return make_error_code(errc::no_such_file_or_directory); - StringRef CurArchiveName = CurrentArchive->getFileName(); + StringRef CurArchiveName = CurrentArchives.front()->getFileName(); if (!Filename.startswith(Twine(CurArchiveName, "(").str())) return make_error_code(errc::no_such_file_or_directory); // Remove the archive name and the parens around the archive member name. Filename = Filename.substr(CurArchiveName.size() + 1).drop_back(); - for (const auto &Child : CurrentArchive->children()) { - if (auto NameOrErr = Child.getName()) - if (*NameOrErr == Filename) { - if (Verbose) - outs() << "\tfound member in current archive.\n"; - return Child.getMemoryBufferRef(); + std::vector<MemoryBufferRef> Buffers; + Buffers.reserve(CurrentArchives.size()); + + for (const auto &CurrentArchive : CurrentArchives) { + for (auto ChildOrErr : CurrentArchive->children()) { + if (std::error_code Err = ChildOrErr.getError()) + return Err; + const auto &Child = *ChildOrErr; + if (auto NameOrErr = Child.getName()) { + if (*NameOrErr == Filename) { + if (Timestamp != sys::TimeValue::PosixZeroTime() && + Timestamp != Child.getLastModified()) { + if (Verbose) + outs() << "\tmember had timestamp mismatch.\n"; + continue; + } + if (Verbose) + outs() << "\tfound member in current archive.\n"; + auto ErrOrMem = Child.getMemoryBufferRef(); + if (auto Err = ErrOrMem.getError()) + return Err; + Buffers.push_back(*ErrOrMem); + } } + } } - return make_error_code(errc::no_such_file_or_directory); + if (Buffers.empty()) + return make_error_code(errc::no_such_file_or_directory); + return Buffers; } -ErrorOr<MemoryBufferRef> -BinaryHolder::MapArchiveAndGetMemberBuffer(StringRef Filename) { +ErrorOr<std::vector<MemoryBufferRef>> +BinaryHolder::MapArchiveAndGetMemberBuffers(StringRef Filename, + sys::TimeValue Timestamp) { StringRef ArchiveFilename = Filename.substr(0, Filename.find('(')); auto ErrOrBuff = MemoryBuffer::getFileOrSTDIN(ArchiveFilename); @@ -83,29 +148,62 @@ BinaryHolder::MapArchiveAndGetMemberBuffer(StringRef Filename) { if (Verbose) outs() << "\topened new archive '" << ArchiveFilename << "'\n"; - auto ErrOrArchive = object::Archive::create((*ErrOrBuff)->getMemBufferRef()); - if (auto Err = ErrOrArchive.getError()) - return Err; - CurrentArchive = std::move(*ErrOrArchive); - CurrentMemoryBuffer = std::move(*ErrOrBuff); + changeBackingMemoryBuffer(std::move(*ErrOrBuff)); + std::vector<MemoryBufferRef> ArchiveBuffers; + auto ErrOrFat = object::MachOUniversalBinary::create( + CurrentMemoryBuffer->getMemBufferRef()); + if (ErrOrFat.getError()) { + // Not a fat binary must be a standard one. + ArchiveBuffers.push_back(CurrentMemoryBuffer->getMemBufferRef()); + } else { + CurrentFatBinary = std::move(*ErrOrFat); + ArchiveBuffers = getMachOFatMemoryBuffers( + ArchiveFilename, *CurrentMemoryBuffer, *CurrentFatBinary); + } - return GetArchiveMemberBuffer(Filename); + for (auto MemRef : ArchiveBuffers) { + auto ErrOrArchive = object::Archive::create(MemRef); + if (auto Err = ErrOrArchive.getError()) + return Err; + CurrentArchives.push_back(std::move(*ErrOrArchive)); + } + return GetArchiveMemberBuffers(Filename, Timestamp); } ErrorOr<const object::ObjectFile &> -BinaryHolder::GetObjectFile(StringRef Filename) { - auto ErrOrMemBufferRef = GetMemoryBufferForFile(Filename); - if (auto Err = ErrOrMemBufferRef.getError()) - return Err; +BinaryHolder::getObjfileForArch(const Triple &T) { + for (const auto &Obj : CurrentObjectFiles) { + if (const auto *MachO = dyn_cast<object::MachOObjectFile>(Obj.get())) { + if (getTriple(*MachO).str() == T.str()) + return *MachO; + } else if (Obj->getArch() == T.getArch()) + return *Obj; + } - auto ErrOrObjectFile = - object::ObjectFile::createObjectFile(*ErrOrMemBufferRef); - if (auto Err = ErrOrObjectFile.getError()) + return make_error_code(object::object_error::arch_not_found); +} + +ErrorOr<std::vector<const object::ObjectFile *>> +BinaryHolder::GetObjectFiles(StringRef Filename, sys::TimeValue Timestamp) { + auto ErrOrMemBufferRefs = GetMemoryBuffersForFile(Filename, Timestamp); + if (auto Err = ErrOrMemBufferRefs.getError()) return Err; - CurrentObjectFile = std::move(*ErrOrObjectFile); - return *CurrentObjectFile; + std::vector<const object::ObjectFile *> Objects; + Objects.reserve(ErrOrMemBufferRefs->size()); + + CurrentObjectFiles.clear(); + for (auto MemBuf : *ErrOrMemBufferRefs) { + auto ErrOrObjectFile = object::ObjectFile::createObjectFile(MemBuf); + if (auto Err = ErrOrObjectFile.getError()) + return Err; + + Objects.push_back(ErrOrObjectFile->get()); + CurrentObjectFiles.push_back(std::move(*ErrOrObjectFile)); + } + + return std::move(Objects); } } } diff --git a/tools/dsymutil/BinaryHolder.h b/tools/dsymutil/BinaryHolder.h index 04871b5d5855..9d7b4bd8787f 100644 --- a/tools/dsymutil/BinaryHolder.h +++ b/tools/dsymutil/BinaryHolder.h @@ -14,11 +14,14 @@ #ifndef LLVM_TOOLS_DSYMUTIL_BINARYHOLDER_H #define LLVM_TOOLS_DSYMUTIL_BINARYHOLDER_H +#include "llvm/ADT/Triple.h" #include "llvm/Object/Archive.h" #include "llvm/Object/Error.h" +#include "llvm/Object/MachOUniversal.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/Errc.h" #include "llvm/Support/ErrorOr.h" +#include "llvm/Support/TimeValue.h" namespace llvm { namespace dsymutil { @@ -35,69 +38,98 @@ namespace dsymutil { /// meaning that a mapping request will invalidate the previous memory /// mapping. class BinaryHolder { - std::unique_ptr<object::Archive> CurrentArchive; + std::vector<std::unique_ptr<object::Archive>> CurrentArchives; std::unique_ptr<MemoryBuffer> CurrentMemoryBuffer; - std::unique_ptr<object::ObjectFile> CurrentObjectFile; + std::vector<std::unique_ptr<object::ObjectFile>> CurrentObjectFiles; + std::unique_ptr<object::MachOUniversalBinary> CurrentFatBinary; bool Verbose; - /// \brief Get the MemoryBufferRef for the file specification in \p - /// Filename from the current archive. + /// Get the MemoryBufferRefs for the file specification in \p + /// Filename from the current archive. Multiple buffers are returned + /// when there are multiple architectures available for the + /// requested file. /// /// This function performs no system calls, it just looks up a /// potential match for the given \p Filename in the currently /// mapped archive if there is one. - ErrorOr<MemoryBufferRef> GetArchiveMemberBuffer(StringRef Filename); + ErrorOr<std::vector<MemoryBufferRef>> + GetArchiveMemberBuffers(StringRef Filename, sys::TimeValue Timestamp); - /// \brief Interpret Filename as an archive member specification, - /// map the corresponding archive to memory and return the - /// MemoryBufferRef corresponding to the described member. - ErrorOr<MemoryBufferRef> MapArchiveAndGetMemberBuffer(StringRef Filename); + /// Interpret Filename as an archive member specification map the + /// corresponding archive to memory and return the MemoryBufferRefs + /// corresponding to the described member. Multiple buffers are + /// returned when there are multiple architectures available for the + /// requested file. + ErrorOr<std::vector<MemoryBufferRef>> + MapArchiveAndGetMemberBuffers(StringRef Filename, sys::TimeValue Timestamp); - /// \brief Return the MemoryBufferRef that holds the memory - /// mapping for the given \p Filename. This function will try to - /// parse archive member specifications of the form - /// /path/to/archive.a(member.o). + /// Return the MemoryBufferRef that holds the memory mapping for the + /// given \p Filename. This function will try to parse archive + /// member specifications of the form /path/to/archive.a(member.o). /// - /// The returned MemoryBufferRef points to a buffer owned by this + /// The returned MemoryBufferRefs points to a buffer owned by this /// object. The buffer is valid until the next call to /// GetMemoryBufferForFile() on this object. - ErrorOr<MemoryBufferRef> GetMemoryBufferForFile(StringRef Filename); + /// Multiple buffers are returned when there are multiple + /// architectures available for the requested file. + ErrorOr<std::vector<MemoryBufferRef>> + GetMemoryBuffersForFile(StringRef Filename, sys::TimeValue Timestamp); + + void changeBackingMemoryBuffer(std::unique_ptr<MemoryBuffer> &&MemBuf); + ErrorOr<const object::ObjectFile &> getObjfileForArch(const Triple &T); public: BinaryHolder(bool Verbose) : Verbose(Verbose) {} - /// \brief Get the ObjectFile designated by the \p Filename. This + /// Get the ObjectFiles designated by the \p Filename. This /// might be an archive member specification of the form /// /path/to/archive.a(member.o). /// /// Calling this function invalidates the previous mapping owned by - /// the BinaryHolder. - ErrorOr<const object::ObjectFile &> GetObjectFile(StringRef Filename); + /// the BinaryHolder. Multiple buffers are returned when there are + /// multiple architectures available for the requested file. + ErrorOr<std::vector<const object::ObjectFile *>> + GetObjectFiles(StringRef Filename, + sys::TimeValue Timestamp = sys::TimeValue::PosixZeroTime()); - /// \brief Wraps GetObjectFile() to return a derived ObjectFile type. + /// Wraps GetObjectFiles() to return a derived ObjectFile type. template <typename ObjectFileType> - ErrorOr<const ObjectFileType &> GetFileAs(StringRef Filename) { - auto ErrOrObjFile = GetObjectFile(Filename); + ErrorOr<std::vector<const ObjectFileType *>> + GetFilesAs(StringRef Filename, + sys::TimeValue Timestamp = sys::TimeValue::PosixZeroTime()) { + auto ErrOrObjFile = GetObjectFiles(Filename, Timestamp); if (auto Err = ErrOrObjFile.getError()) return Err; - if (const auto *Derived = dyn_cast<ObjectFileType>(CurrentObjectFile.get())) - return *Derived; - return make_error_code(object::object_error::invalid_file_type); + + std::vector<const ObjectFileType *> Objects; + Objects.reserve((*ErrOrObjFile).size()); + for (const auto &Obj : *ErrOrObjFile) { + const auto *Derived = dyn_cast<ObjectFileType>(Obj); + if (!Derived) + return make_error_code(object::object_error::invalid_file_type); + Objects.push_back(Derived); + } + return std::move(Objects); } - /// \brief Access the currently owned ObjectFile. As successfull - /// call to GetObjectFile() or GetFileAs() must have been performed - /// before calling this. - const object::ObjectFile &Get() { - assert(CurrentObjectFile); - return *CurrentObjectFile; + /// Access the currently owned ObjectFile with architecture \p T. As + /// successfull call to GetObjectFiles() or GetFilesAs() must have + /// been performed before calling this. + ErrorOr<const object::ObjectFile &> Get(const Triple &T) { + return getObjfileForArch(T); } - /// \brief Access to a derived version of the currently owned + /// Access to a derived version of the currently owned /// ObjectFile. The conversion must be known to be valid. - template <typename ObjectFileType> const ObjectFileType &GetAs() { - return cast<ObjectFileType>(*CurrentObjectFile); + template <typename ObjectFileType> + ErrorOr<const ObjectFileType &> GetAs(const Triple &T) { + auto ErrOrObj = Get(T); + if (auto Err = ErrOrObj.getError()) + return Err; + return cast<ObjectFileType>(*ErrOrObj); } + + static Triple getTriple(const object::MachOObjectFile &Obj); }; } } diff --git a/tools/dsymutil/CMakeLists.txt b/tools/dsymutil/CMakeLists.txt index 88f9f1f083db..0f45d20de22c 100644 --- a/tools/dsymutil/CMakeLists.txt +++ b/tools/dsymutil/CMakeLists.txt @@ -14,5 +14,6 @@ add_llvm_tool(llvm-dsymutil DebugMap.cpp DwarfLinker.cpp MachODebugMapParser.cpp + MachOUtils.cpp ) diff --git a/tools/dsymutil/DebugMap.cpp b/tools/dsymutil/DebugMap.cpp index e5cc87b3f318..4717085f4322 100644 --- a/tools/dsymutil/DebugMap.cpp +++ b/tools/dsymutil/DebugMap.cpp @@ -20,8 +20,9 @@ namespace dsymutil { using namespace llvm::object; -DebugMapObject::DebugMapObject(StringRef ObjectFilename) - : Filename(ObjectFilename) {} +DebugMapObject::DebugMapObject(StringRef ObjectFilename, + sys::TimeValue Timestamp) + : Filename(ObjectFilename), Timestamp(Timestamp) {} bool DebugMapObject::addSymbol(StringRef Name, uint64_t ObjectAddress, uint64_t LinkedAddress, uint32_t Size) { @@ -58,8 +59,9 @@ void DebugMapObject::print(raw_ostream &OS) const { void DebugMapObject::dump() const { print(errs()); } #endif -DebugMapObject &DebugMap::addDebugMapObject(StringRef ObjectFilePath) { - Objects.emplace_back(new DebugMapObject(ObjectFilePath)); +DebugMapObject &DebugMap::addDebugMapObject(StringRef ObjectFilePath, + sys::TimeValue Timestamp) { + Objects.emplace_back(new DebugMapObject(ObjectFilePath, Timestamp)); return *Objects.back(); } @@ -95,7 +97,7 @@ struct YAMLContext { }; } -ErrorOr<std::unique_ptr<DebugMap>> +ErrorOr<std::vector<std::unique_ptr<DebugMap>>> DebugMap::parseYAMLDebugMap(StringRef InputFile, StringRef PrependPath, bool Verbose) { auto ErrOrFile = MemoryBuffer::getFileOrSTDIN(InputFile); @@ -112,8 +114,9 @@ DebugMap::parseYAMLDebugMap(StringRef InputFile, StringRef PrependPath, if (auto EC = yin.error()) return EC; - - return std::move(Res); + std::vector<std::unique_ptr<DebugMap>> Result; + Result.push_back(std::move(Res)); + return std::move(Result); } } @@ -121,11 +124,12 @@ namespace yaml { // Normalize/Denormalize between YAML and a DebugMapObject. struct MappingTraits<dsymutil::DebugMapObject>::YamlDMO { - YamlDMO(IO &io) {} + YamlDMO(IO &io) { Timestamp = 0; } YamlDMO(IO &io, dsymutil::DebugMapObject &Obj); dsymutil::DebugMapObject denormalize(IO &IO); std::string Filename; + sys::TimeValue::SecondsType Timestamp; std::vector<dsymutil::DebugMapObject::YAMLSymbolMapping> Entries; }; @@ -141,6 +145,7 @@ void MappingTraits<dsymutil::DebugMapObject>::mapping( IO &io, dsymutil::DebugMapObject &DMO) { MappingNormalization<YamlDMO, dsymutil::DebugMapObject> Norm(io, DMO); io.mapRequired("filename", Norm->Filename); + io.mapOptional("timestamp", Norm->Timestamp); io.mapRequired("symbols", Norm->Entries); } @@ -174,9 +179,10 @@ SequenceTraits<std::vector<std::unique_ptr<dsymutil::DebugMapObject>>>::element( void MappingTraits<dsymutil::DebugMap>::mapping(IO &io, dsymutil::DebugMap &DM) { io.mapRequired("triple", DM.BinaryTriple); - io.mapOptional("objects", DM.Objects); + io.mapOptional("binary-path", DM.BinaryPath); if (void *Ctxt = io.getContext()) reinterpret_cast<YAMLContext *>(Ctxt)->BinaryTriple = DM.BinaryTriple; + io.mapOptional("objects", DM.Objects); } void MappingTraits<std::unique_ptr<dsymutil::DebugMap>>::mapping( @@ -184,14 +190,16 @@ void MappingTraits<std::unique_ptr<dsymutil::DebugMap>>::mapping( if (!DM) DM.reset(new DebugMap()); io.mapRequired("triple", DM->BinaryTriple); - io.mapOptional("objects", DM->Objects); + io.mapOptional("binary-path", DM->BinaryPath); if (void *Ctxt = io.getContext()) reinterpret_cast<YAMLContext *>(Ctxt)->BinaryTriple = DM->BinaryTriple; + io.mapOptional("objects", DM->Objects); } MappingTraits<dsymutil::DebugMapObject>::YamlDMO::YamlDMO( IO &io, dsymutil::DebugMapObject &Obj) { Filename = Obj.Filename; + Timestamp = Obj.getTimestamp().toEpochTime(); Entries.reserve(Obj.Symbols.size()); for (auto &Entry : Obj.Symbols) Entries.push_back(std::make_pair(Entry.getKey(), Entry.getValue())); @@ -205,11 +213,11 @@ MappingTraits<dsymutil::DebugMapObject>::YamlDMO::denormalize(IO &IO) { StringMap<uint64_t> SymbolAddresses; sys::path::append(Path, Filename); - auto ErrOrObjectFile = BinHolder.GetObjectFile(Path); - if (auto EC = ErrOrObjectFile.getError()) { + auto ErrOrObjectFiles = BinHolder.GetObjectFiles(Path); + if (auto EC = ErrOrObjectFiles.getError()) { llvm::errs() << "warning: Unable to open " << Path << " " << EC.message() << '\n'; - } else { + } else if (auto ErrOrObjectFile = BinHolder.Get(Ctxt.BinaryTriple)) { // Rewrite the object file symbol addresses in the debug map. The // YAML input is mainly used to test llvm-dsymutil without // requiring binaries checked-in. If we generate the object files @@ -224,7 +232,9 @@ MappingTraits<dsymutil::DebugMapObject>::YamlDMO::denormalize(IO &IO) { } } - dsymutil::DebugMapObject Res(Path); + sys::TimeValue TV; + TV.fromEpochTime(Timestamp); + dsymutil::DebugMapObject Res(Path, TV); for (auto &Entry : Entries) { auto &Mapping = Entry.second; uint64_t ObjAddress = Mapping.ObjectAddress; diff --git a/tools/dsymutil/DebugMap.h b/tools/dsymutil/DebugMap.h index d0edbabb404b..06ac5a503dcd 100644 --- a/tools/dsymutil/DebugMap.h +++ b/tools/dsymutil/DebugMap.h @@ -29,6 +29,7 @@ #include "llvm/Support/ErrorOr.h" #include "llvm/Support/Format.h" #include "llvm/Support/Path.h" +#include "llvm/Support/TimeValue.h" #include "llvm/Support/YAMLTraits.h" #include <vector> @@ -65,6 +66,7 @@ class DebugMapObject; /// } class DebugMap { Triple BinaryTriple; + std::string BinaryPath; typedef std::vector<std::unique_ptr<DebugMapObject>> ObjectContainer; ObjectContainer Objects; @@ -75,7 +77,8 @@ class DebugMap { DebugMap() = default; ///@} public: - DebugMap(const Triple &BinaryTriple) : BinaryTriple(BinaryTriple) {} + DebugMap(const Triple &BinaryTriple, StringRef BinaryPath) + : BinaryTriple(BinaryTriple), BinaryPath(BinaryPath) {} typedef ObjectContainer::const_iterator const_iterator; @@ -89,10 +92,13 @@ public: /// This function adds an DebugMapObject to the list owned by this /// debug map. - DebugMapObject &addDebugMapObject(StringRef ObjectFilePath); + DebugMapObject &addDebugMapObject(StringRef ObjectFilePath, + sys::TimeValue Timestamp); const Triple &getTriple() const { return BinaryTriple; } + StringRef getBinaryPath() const { return BinaryPath; } + void print(raw_ostream &OS) const; #ifndef NDEBUG @@ -100,7 +106,7 @@ public: #endif /// Read a debug map for \a InputFile. - static ErrorOr<std::unique_ptr<DebugMap>> + static ErrorOr<std::vector<std::unique_ptr<DebugMap>>> parseYAMLDebugMap(StringRef InputFile, StringRef PrependPath, bool Verbose); }; @@ -139,6 +145,8 @@ public: llvm::StringRef getObjectFilename() const { return Filename; } + sys::TimeValue getTimestamp() const { return Timestamp; } + iterator_range<StringMap<SymbolMapping>::const_iterator> symbols() const { return make_range(Symbols.begin(), Symbols.end()); } @@ -150,9 +158,10 @@ public: private: friend class DebugMap; /// DebugMapObjects can only be constructed by the owning DebugMap. - DebugMapObject(StringRef ObjectFilename); + DebugMapObject(StringRef ObjectFilename, sys::TimeValue Timestamp); std::string Filename; + sys::TimeValue Timestamp; StringMap<SymbolMapping> Symbols; DenseMap<uint64_t, DebugMapEntry *> AddressToMapping; @@ -167,12 +176,14 @@ private: public: DebugMapObject &operator=(DebugMapObject RHS) { std::swap(Filename, RHS.Filename); + std::swap(Timestamp, RHS.Timestamp); std::swap(Symbols, RHS.Symbols); std::swap(AddressToMapping, RHS.AddressToMapping); return *this; } DebugMapObject(DebugMapObject &&RHS) { Filename = std::move(RHS.Filename); + Timestamp = std::move(RHS.Timestamp); Symbols = std::move(RHS.Symbols); AddressToMapping = std::move(RHS.AddressToMapping); } diff --git a/tools/dsymutil/DwarfLinker.cpp b/tools/dsymutil/DwarfLinker.cpp index 6e9087c70eca..8c5f7946a425 100644 --- a/tools/dsymutil/DwarfLinker.cpp +++ b/tools/dsymutil/DwarfLinker.cpp @@ -10,11 +10,14 @@ #include "BinaryHolder.h" #include "DebugMap.h" #include "dsymutil.h" +#include "MachOUtils.h" +#include "NonRelocatableStringpool.h" #include "llvm/ADT/IntervalMap.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/DIE.h" +#include "llvm/Config/config.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h" #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" @@ -28,6 +31,7 @@ #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCTargetOptionsCommandFlags.h" #include "llvm/Object/MachO.h" #include "llvm/Support/Dwarf.h" #include "llvm/Support/LEB128.h" @@ -42,17 +46,6 @@ namespace dsymutil { namespace { -void warn(const Twine &Warning, const Twine &Context) { - errs() << Twine("while processing ") + Context + ":\n"; - errs() << Twine("warning: ") + Warning + "\n"; -} - -bool error(const Twine &Error, const Twine &Context) { - errs() << Twine("while processing ") + Context + ":\n"; - errs() << Twine("error: ") + Error + "\n"; - return false; -} - template <typename KeyT, typename ValT> using HalfOpenIntervalMap = IntervalMap<KeyT, ValT, IntervalMapImpl::NodeSizer<KeyT, ValT>::LeafSize, @@ -80,6 +73,112 @@ struct PatchLocation { } }; +class CompileUnit; +struct DeclMapInfo; + +/// A DeclContext is a named program scope that is used for ODR +/// uniquing of types. +/// The set of DeclContext for the ODR-subject parts of a Dwarf link +/// is expanded (and uniqued) with each new object file processed. We +/// need to determine the context of each DIE in an linked object file +/// to see if the corresponding type has already been emitted. +/// +/// The contexts are conceptually organised as a tree (eg. a function +/// scope is contained in a namespace scope that contains other +/// scopes), but storing/accessing them in an actual tree is too +/// inefficient: we need to be able to very quickly query a context +/// for a given child context by name. Storing a StringMap in each +/// DeclContext would be too space inefficient. +/// The solution here is to give each DeclContext a link to its parent +/// (this allows to walk up the tree), but to query the existance of a +/// specific DeclContext using a separate DenseMap keyed on the hash +/// of the fully qualified name of the context. +class DeclContext { + unsigned QualifiedNameHash; + uint32_t Line; + uint32_t ByteSize; + uint16_t Tag; + StringRef Name; + StringRef File; + const DeclContext &Parent; + const DWARFDebugInfoEntryMinimal *LastSeenDIE; + uint32_t LastSeenCompileUnitID; + uint32_t CanonicalDIEOffset; + + friend DeclMapInfo; + +public: + typedef DenseSet<DeclContext *, DeclMapInfo> Map; + + DeclContext() + : QualifiedNameHash(0), Line(0), ByteSize(0), + Tag(dwarf::DW_TAG_compile_unit), Name(), File(), Parent(*this), + LastSeenDIE(nullptr), LastSeenCompileUnitID(0), CanonicalDIEOffset(0) {} + + DeclContext(unsigned Hash, uint32_t Line, uint32_t ByteSize, uint16_t Tag, + StringRef Name, StringRef File, const DeclContext &Parent, + const DWARFDebugInfoEntryMinimal *LastSeenDIE = nullptr, + unsigned CUId = 0) + : QualifiedNameHash(Hash), Line(Line), ByteSize(ByteSize), Tag(Tag), + Name(Name), File(File), Parent(Parent), LastSeenDIE(LastSeenDIE), + LastSeenCompileUnitID(CUId), CanonicalDIEOffset(0) {} + + uint32_t getQualifiedNameHash() const { return QualifiedNameHash; } + + bool setLastSeenDIE(CompileUnit &U, const DWARFDebugInfoEntryMinimal *Die); + + uint32_t getCanonicalDIEOffset() const { return CanonicalDIEOffset; } + void setCanonicalDIEOffset(uint32_t Offset) { CanonicalDIEOffset = Offset; } + + uint16_t getTag() const { return Tag; } + StringRef getName() const { return Name; } +}; + +/// Info type for the DenseMap storing the DeclContext pointers. +struct DeclMapInfo : private DenseMapInfo<DeclContext *> { + using DenseMapInfo<DeclContext *>::getEmptyKey; + using DenseMapInfo<DeclContext *>::getTombstoneKey; + + static unsigned getHashValue(const DeclContext *Ctxt) { + return Ctxt->QualifiedNameHash; + } + + static bool isEqual(const DeclContext *LHS, const DeclContext *RHS) { + if (RHS == getEmptyKey() || RHS == getTombstoneKey()) + return RHS == LHS; + return LHS->QualifiedNameHash == RHS->QualifiedNameHash && + LHS->Line == RHS->Line && LHS->ByteSize == RHS->ByteSize && + LHS->Name.data() == RHS->Name.data() && + LHS->File.data() == RHS->File.data() && + LHS->Parent.QualifiedNameHash == RHS->Parent.QualifiedNameHash; + } +}; + +/// This class gives a tree-like API to the DenseMap that stores the +/// DeclContext objects. It also holds the BumpPtrAllocator where +/// these objects will be allocated. +class DeclContextTree { + BumpPtrAllocator Allocator; + DeclContext Root; + DeclContext::Map Contexts; + +public: + /// Get the child of \a Context described by \a DIE in \a Unit. The + /// required strings will be interned in \a StringPool. + /// \returns The child DeclContext along with one bit that is set if + /// this context is invalid. + /// An invalid context means it shouldn't be considered for uniquing, but its + /// not returning null, because some children of that context might be + /// uniquing candidates. FIXME: The invalid bit along the return value is to + /// emulate some dsymutil-classic functionality. + PointerIntPair<DeclContext *, 1> + getChildDeclContext(DeclContext &Context, + const DWARFDebugInfoEntryMinimal *DIE, CompileUnit &Unit, + NonRelocatableStringpool &StringPool, bool InClangModule); + + DeclContext &getRoot() { return Root; } +}; + /// \brief Stores all information relating to a compile unit, be it in /// its original instance in the object file to its brand new cloned /// and linked DIE tree. @@ -88,16 +187,28 @@ public: /// \brief Information gathered about a DIE in the object file. struct DIEInfo { int64_t AddrAdjust; ///< Address offset to apply to the described entity. + DeclContext *Ctxt; ///< ODR Declaration context. DIE *Clone; ///< Cloned version of that DIE. uint32_t ParentIdx; ///< The index of this DIE's parent. - bool Keep; ///< Is the DIE part of the linked output? - bool InDebugMap; ///< Was this DIE's entity found in the map? + bool Keep : 1; ///< Is the DIE part of the linked output? + bool InDebugMap : 1;///< Was this DIE's entity found in the map? + bool Prune : 1; ///< Is this a pure forward declaration we can strip? }; - CompileUnit(DWARFUnit &OrigUnit, unsigned ID) + CompileUnit(DWARFUnit &OrigUnit, unsigned ID, bool CanUseODR, + StringRef ClangModuleName) : OrigUnit(OrigUnit), ID(ID), LowPc(UINT64_MAX), HighPc(0), RangeAlloc(), - Ranges(RangeAlloc) { + Ranges(RangeAlloc), ClangModuleName(ClangModuleName) { Info.resize(OrigUnit.getNumDIEs()); + + const auto *CUDie = OrigUnit.getUnitDIE(false); + unsigned Lang = CUDie->getAttributeValueAsUnsignedConstant( + &OrigUnit, dwarf::DW_AT_language, 0); + HasODR = CanUseODR && (Lang == dwarf::DW_LANG_C_plus_plus || + Lang == dwarf::DW_LANG_C_plus_plus_03 || + Lang == dwarf::DW_LANG_C_plus_plus_11 || + Lang == dwarf::DW_LANG_C_plus_plus_14 || + Lang == dwarf::DW_LANG_ObjC_plus_plus); } CompileUnit(CompileUnit &&RHS) @@ -116,6 +227,10 @@ public: DIE *getOutputUnitDIE() const { return CUDie; } void setOutputUnitDIE(DIE *Die) { CUDie = Die; } + bool hasODR() const { return HasODR; } + bool isClangModule() const { return !ClangModuleName.empty(); } + const std::string &getClangModuleName() const { return ClangModuleName; } + DIEInfo &getInfo(unsigned Idx) { return Info[Idx]; } const DIEInfo &getInfo(unsigned Idx) const { return Info[Idx]; } @@ -139,6 +254,14 @@ public: return LocationAttributes; } + void setHasInterestingContent() { HasInterestingContent = true; } + bool hasInterestingContent() { return HasInterestingContent; } + + /// Mark every DIE in this unit as kept. This function also + /// marks variables as InDebugMap so that they appear in the + /// reconstructed accelerator tables. + void markEverythingAsKept(); + /// \brief Compute the end offset for this unit. Must be /// called after the CU's DIEs have been cloned. /// \returns the next unit offset (which is also the current @@ -147,9 +270,10 @@ public: /// \brief Keep track of a forward reference to DIE \p Die in \p /// RefUnit by \p Attr. The attribute should be fixed up later to - /// point to the absolute offset of \p Die in the debug_info section. + /// point to the absolute offset of \p Die in the debug_info section + /// or to the canonical offset of \p Ctxt if it is non-null. void noteForwardReference(DIE *Die, const CompileUnit *RefUnit, - PatchLocation Attr); + DeclContext *Ctxt, PatchLocation Attr); /// \brief Apply all fixups recored by noteForwardReference(). void fixupForwardReferences(); @@ -190,11 +314,27 @@ public: const std::vector<AccelInfo> &getPubnames() const { return Pubnames; } const std::vector<AccelInfo> &getPubtypes() const { return Pubtypes; } + /// Get the full path for file \a FileNum in the line table + const char *getResolvedPath(unsigned FileNum) { + if (FileNum >= ResolvedPaths.size()) + return nullptr; + return ResolvedPaths[FileNum].size() ? ResolvedPaths[FileNum].c_str() + : nullptr; + } + + /// Set the fully resolved path for the line-table's file \a FileNum + /// to \a Path. + void setResolvedPath(unsigned FileNum, const std::string &Path) { + if (ResolvedPaths.size() <= FileNum) + ResolvedPaths.resize(FileNum + 1); + ResolvedPaths[FileNum] = Path; + } + private: DWARFUnit &OrigUnit; unsigned ID; - std::vector<DIEInfo> Info; ///< DIE info indexed by DIE index. - DIE *CUDie; ///< Root of the linked DIE tree. + std::vector<DIEInfo> Info; ///< DIE info indexed by DIE index. + DIE *CUDie; ///< Root of the linked DIE tree. uint64_t StartOffset; uint64_t NextUnitOffset; @@ -208,8 +348,8 @@ private: /// The offsets for the attributes in this array couldn't be set while /// cloning because for cross-cu forward refences the target DIE's /// offset isn't known you emit the reference attribute. - std::vector<std::tuple<DIE *, const CompileUnit *, PatchLocation>> - ForwardDIEReferences; + std::vector<std::tuple<DIE *, const CompileUnit *, DeclContext *, + PatchLocation>> ForwardDIEReferences; FunctionIntervals::Allocator RangeAlloc; /// \brief The ranges in that interval map are the PC ranges for @@ -236,8 +376,24 @@ private: std::vector<AccelInfo> Pubnames; std::vector<AccelInfo> Pubtypes; /// @} + + /// Cached resolved paths from the line table. + std::vector<std::string> ResolvedPaths; + + /// Is this unit subject to the ODR rule? + bool HasODR; + /// Did a DIE actually contain a valid reloc? + bool HasInterestingContent; + /// If this is a Clang module, this holds the module's name. + std::string ClangModuleName; }; +void CompileUnit::markEverythingAsKept() { + for (auto &I : Info) + // Mark everything that wasn't explicity marked for pruning. + I.Keep = !I.Prune; +} + uint64_t CompileUnit::computeNextUnitOffset() { NextUnitOffset = StartOffset + 11 /* Header size */; // The root DIE might be null, meaning that the Unit had nothing to @@ -251,8 +407,8 @@ uint64_t CompileUnit::computeNextUnitOffset() { /// \brief Keep track of a forward cross-cu reference from this unit /// to \p Die that lives in \p RefUnit. void CompileUnit::noteForwardReference(DIE *Die, const CompileUnit *RefUnit, - PatchLocation Attr) { - ForwardDIEReferences.emplace_back(Die, RefUnit, Attr); + DeclContext *Ctxt, PatchLocation Attr) { + ForwardDIEReferences.emplace_back(Die, RefUnit, Ctxt, Attr); } /// \brief Apply all fixups recorded by noteForwardReference(). @@ -261,8 +417,12 @@ void CompileUnit::fixupForwardReferences() { DIE *RefDie; const CompileUnit *RefUnit; PatchLocation Attr; - std::tie(RefDie, RefUnit, Attr) = Ref; - Attr.set(RefDie->getOffset() + RefUnit->getStartOffset()); + DeclContext *Ctxt; + std::tie(RefDie, RefUnit, Ctxt, Attr) = Ref; + if (Ctxt && Ctxt->getCanonicalDIEOffset()) + Attr.set(Ctxt->getCanonicalDIEOffset()); + else + Attr.set(RefDie->getOffset() + RefUnit->getStartOffset()); } } @@ -298,91 +458,6 @@ void CompileUnit::addTypeAccelerator(const DIE *Die, const char *Name, Pubtypes.emplace_back(Name, Die, Offset, false); } -/// \brief A string table that doesn't need relocations. -/// -/// We are doing a final link, no need for a string table that -/// has relocation entries for every reference to it. This class -/// provides this ablitity by just associating offsets with -/// strings. -class NonRelocatableStringpool { -public: - /// \brief Entries are stored into the StringMap and simply linked - /// together through the second element of this pair in order to - /// keep track of insertion order. - typedef StringMap<std::pair<uint32_t, StringMapEntryBase *>, BumpPtrAllocator> - MapTy; - - NonRelocatableStringpool() - : CurrentEndOffset(0), Sentinel(0), Last(&Sentinel) { - // Legacy dsymutil puts an empty string at the start of the line - // table. - getStringOffset(""); - } - - /// \brief Get the offset of string \p S in the string table. This - /// can insert a new element or return the offset of a preexisitng - /// one. - uint32_t getStringOffset(StringRef S); - - /// \brief Get permanent storage for \p S (but do not necessarily - /// emit \p S in the output section). - /// \returns The StringRef that points to permanent storage to use - /// in place of \p S. - StringRef internString(StringRef S); - - // \brief Return the first entry of the string table. - const MapTy::MapEntryTy *getFirstEntry() const { - return getNextEntry(&Sentinel); - } - - // \brief Get the entry following \p E in the string table or null - // if \p E was the last entry. - const MapTy::MapEntryTy *getNextEntry(const MapTy::MapEntryTy *E) const { - return static_cast<const MapTy::MapEntryTy *>(E->getValue().second); - } - - uint64_t getSize() { return CurrentEndOffset; } - -private: - MapTy Strings; - uint32_t CurrentEndOffset; - MapTy::MapEntryTy Sentinel, *Last; -}; - -/// \brief Get the offset of string \p S in the string table. This -/// can insert a new element or return the offset of a preexisitng -/// one. -uint32_t NonRelocatableStringpool::getStringOffset(StringRef S) { - if (S.empty() && !Strings.empty()) - return 0; - - std::pair<uint32_t, StringMapEntryBase *> Entry(0, nullptr); - MapTy::iterator It; - bool Inserted; - - // A non-empty string can't be at offset 0, so if we have an entry - // with a 0 offset, it must be a previously interned string. - std::tie(It, Inserted) = Strings.insert(std::make_pair(S, Entry)); - if (Inserted || It->getValue().first == 0) { - // Set offset and chain at the end of the entries list. - It->getValue().first = CurrentEndOffset; - CurrentEndOffset += S.size() + 1; // +1 for the '\0'. - Last->getValue().second = &*It; - Last = &*It; - } - return It->getValue().first; -} - -/// \brief Put \p S into the StringMap so that it gets permanent -/// storage, but do not actually link it in the chain of elements -/// that go into the output section. A latter call to -/// getStringOffset() with the same string will chain it though. -StringRef NonRelocatableStringpool::internString(StringRef S) { - std::pair<uint32_t, StringMapEntryBase *> Entry(0, nullptr); - auto InsertResult = Strings.insert(std::make_pair(S, Entry)); - return InsertResult.first->getKey(); -} - /// \brief The Dwarf streaming logic /// /// All interactions with the MC layer that is used to build the debug @@ -425,7 +500,7 @@ public: bool init(Triple TheTriple, StringRef OutputFilename); /// \brief Dump the file to the disk. - bool finish(); + bool finish(const DebugMap &); AsmPrinter &getAsmPrinter() const { return *Asm; } @@ -445,7 +520,7 @@ public: /// \brief Emit the abbreviation table \p Abbrevs to the /// debug_abbrev section. - void emitAbbrevs(const std::vector<DIEAbbrev *> &Abbrevs); + void emitAbbrevs(const std::vector<std::unique_ptr<DIEAbbrev>> &Abbrevs); /// \brief Emit the string table described by \p Pool. void emitStrings(const NonRelocatableStringpool &Pool); @@ -472,7 +547,8 @@ public: /// \brief Emit the line table described in \p Rows into the /// debug_line section. - void emitLineTableForUnit(StringRef PrologueBytes, unsigned MinInstLength, + void emitLineTableForUnit(MCDwarfLineTableParams Params, + StringRef PrologueBytes, unsigned MinInstLength, std::vector<DWARFDebugLine::Row> &Rows, unsigned AdddressSize); @@ -543,9 +619,11 @@ bool DwarfStreamer::init(Triple TheTriple, StringRef OutputFilename) { if (EC) return error(Twine(OutputFilename) + ": " + EC.message(), Context); - MS = TheTarget->createMCObjectStreamer(TheTriple, *MC, *MAB, *OutFile, MCE, - *MSTI, false, - /*DWARFMustBeAtTheEnd*/ false); + MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags(); + MS = TheTarget->createMCObjectStreamer( + TheTriple, *MC, *MAB, *OutFile, MCE, *MSTI, MCOptions.MCRelaxAll, + MCOptions.MCIncrementalLinkerCompatible, + /*DWARFMustBeAtTheEnd*/ false); if (!MS) return error("no object streamer for target " + TripleName, Context); @@ -566,7 +644,10 @@ bool DwarfStreamer::init(Triple TheTriple, StringRef OutputFilename) { return true; } -bool DwarfStreamer::finish() { +bool DwarfStreamer::finish(const DebugMap &DM) { + if (DM.getTriple().isOSDarwin() && !DM.getBinaryPath().empty()) + return MachOUtils::generateDsymCompanion(DM, *MS, *OutFile); + MS->Finish(); return true; } @@ -605,7 +686,8 @@ void DwarfStreamer::emitCompileUnitHeader(CompileUnit &Unit) { /// \brief Emit the \p Abbrevs array as the shared abbreviation table /// for the linked Dwarf file. -void DwarfStreamer::emitAbbrevs(const std::vector<DIEAbbrev *> &Abbrevs) { +void DwarfStreamer::emitAbbrevs( + const std::vector<std::unique_ptr<DIEAbbrev>> &Abbrevs) { MS->SwitchSection(MOFI->getDwarfAbbrevSection()); Asm->emitDwarfAbbrevs(Abbrevs); } @@ -637,7 +719,7 @@ void DwarfStreamer::emitRangesEntries( MS->SwitchSection(MC->getObjectFileInfo()->getDwarfRangesSection()); // Offset each range by the right amount. - int64_t PcOffset = FuncRange.value() + UnitPcOffset; + int64_t PcOffset = Entries.empty() ? 0 : FuncRange.value() + UnitPcOffset; for (const auto &Range : Entries) { if (Range.isBaseAddressSelectionEntry(AddressSize)) { warn("unsupported base address selection operation", @@ -796,7 +878,8 @@ void DwarfStreamer::emitLocationsForUnit(const CompileUnit &Unit, } } -void DwarfStreamer::emitLineTableForUnit(StringRef PrologueBytes, +void DwarfStreamer::emitLineTableForUnit(MCDwarfLineTableParams Params, + StringRef PrologueBytes, unsigned MinInstLength, std::vector<DWARFDebugLine::Row> &Rows, unsigned PointerSize) { @@ -819,7 +902,7 @@ void DwarfStreamer::emitLineTableForUnit(StringRef PrologueBytes, if (Rows.empty()) { // We only have the dummy entry, dsymutil emits an entry with a 0 // address in that case. - MCDwarfLineAddr::Encode(*MC, INT64_MAX, 0, EncodingOS); + MCDwarfLineAddr::Encode(*MC, Params, INT64_MAX, 0, EncodingOS); MS->EmitBytes(EncodingOS.str()); LineSectionSize += EncodingBuffer.size(); MS->EmitLabel(LineEndSym); @@ -901,11 +984,10 @@ void DwarfStreamer::emitLineTableForUnit(StringRef PrologueBytes, int64_t LineDelta = int64_t(Row.Line) - LastLine; if (!Row.EndSequence) { - MCDwarfLineAddr::Encode(*MC, LineDelta, AddressDelta, EncodingOS); + MCDwarfLineAddr::Encode(*MC, Params, LineDelta, AddressDelta, EncodingOS); MS->EmitBytes(EncodingOS.str()); LineSectionSize += EncodingBuffer.size(); EncodingBuffer.resize(0); - EncodingOS.resync(); Address = Row.Address; LastLine = Row.Line; RowsSinceLastSequence++; @@ -920,11 +1002,10 @@ void DwarfStreamer::emitLineTableForUnit(StringRef PrologueBytes, MS->EmitULEB128IntValue(AddressDelta); LineSectionSize += 1 + getULEB128Size(AddressDelta); } - MCDwarfLineAddr::Encode(*MC, INT64_MAX, 0, EncodingOS); + MCDwarfLineAddr::Encode(*MC, Params, INT64_MAX, 0, EncodingOS); MS->EmitBytes(EncodingOS.str()); LineSectionSize += EncodingBuffer.size(); EncodingBuffer.resize(0); - EncodingOS.resync(); Address = -1ULL; LastLine = FileNum = IsStatement = 1; RowsSinceLastSequence = Column = Isa = 0; @@ -932,11 +1013,10 @@ void DwarfStreamer::emitLineTableForUnit(StringRef PrologueBytes, } if (RowsSinceLastSequence) { - MCDwarfLineAddr::Encode(*MC, INT64_MAX, 0, EncodingOS); + MCDwarfLineAddr::Encode(*MC, Params, INT64_MAX, 0, EncodingOS); MS->EmitBytes(EncodingOS.str()); LineSectionSize += EncodingBuffer.size(); EncodingBuffer.resize(0); - EncodingOS.resync(); } MS->EmitLabel(LineEndSym); @@ -1035,14 +1115,12 @@ public: : OutputFilename(OutputFilename), Options(Options), BinHolder(Options.Verbose), LastCIEOffset(0) {} - ~DwarfLinker() { - for (auto *Abbrev : Abbreviations) - delete Abbrev; - } - /// \brief Link the contents of the DebugMap. bool link(const DebugMap &); + void reportWarning(const Twine &Warning, const DWARFUnit *Unit = nullptr, + const DWARFDebugInfoEntryMinimal *DIE = nullptr) const; + private: /// \brief Called at the start of a debug object link. void startDebugObject(DWARFContext &, DebugMapObject &); @@ -1050,79 +1128,124 @@ private: /// \brief Called at the end of a debug object link. void endDebugObject(); - /// \defgroup FindValidRelocations Translate debug map into a list - /// of relevant relocations - /// - /// @{ - struct ValidReloc { - uint32_t Offset; - uint32_t Size; - uint64_t Addend; - const DebugMapObject::DebugMapEntry *Mapping; + /// Keeps track of relocations. + class RelocationManager { + struct ValidReloc { + uint32_t Offset; + uint32_t Size; + uint64_t Addend; + const DebugMapObject::DebugMapEntry *Mapping; - ValidReloc(uint32_t Offset, uint32_t Size, uint64_t Addend, - const DebugMapObject::DebugMapEntry *Mapping) - : Offset(Offset), Size(Size), Addend(Addend), Mapping(Mapping) {} + ValidReloc(uint32_t Offset, uint32_t Size, uint64_t Addend, + const DebugMapObject::DebugMapEntry *Mapping) + : Offset(Offset), Size(Size), Addend(Addend), Mapping(Mapping) {} - bool operator<(const ValidReloc &RHS) const { return Offset < RHS.Offset; } - }; + bool operator<(const ValidReloc &RHS) const { + return Offset < RHS.Offset; + } + }; - /// \brief The valid relocations for the current DebugMapObject. - /// This vector is sorted by relocation offset. - std::vector<ValidReloc> ValidRelocs; + DwarfLinker &Linker; - /// \brief Index into ValidRelocs of the next relocation to - /// consider. As we walk the DIEs in acsending file offset and as - /// ValidRelocs is sorted by file offset, keeping this index - /// uptodate is all we have to do to have a cheap lookup during the - /// root DIE selection and during DIE cloning. - unsigned NextValidReloc; + /// \brief The valid relocations for the current DebugMapObject. + /// This vector is sorted by relocation offset. + std::vector<ValidReloc> ValidRelocs; - bool findValidRelocsInDebugInfo(const object::ObjectFile &Obj, - const DebugMapObject &DMO); + /// \brief Index into ValidRelocs of the next relocation to + /// consider. As we walk the DIEs in acsending file offset and as + /// ValidRelocs is sorted by file offset, keeping this index + /// uptodate is all we have to do to have a cheap lookup during the + /// root DIE selection and during DIE cloning. + unsigned NextValidReloc; - bool findValidRelocs(const object::SectionRef &Section, - const object::ObjectFile &Obj, - const DebugMapObject &DMO); + public: + RelocationManager(DwarfLinker &Linker) + : Linker(Linker), NextValidReloc(0) {} - void findValidRelocsMachO(const object::SectionRef &Section, - const object::MachOObjectFile &Obj, - const DebugMapObject &DMO); - /// @} + bool hasValidRelocs() const { return !ValidRelocs.empty(); } + /// \brief Reset the NextValidReloc counter. + void resetValidRelocs() { NextValidReloc = 0; } + + /// \defgroup FindValidRelocations Translate debug map into a list + /// of relevant relocations + /// + /// @{ + bool findValidRelocsInDebugInfo(const object::ObjectFile &Obj, + const DebugMapObject &DMO); + + bool findValidRelocs(const object::SectionRef &Section, + const object::ObjectFile &Obj, + const DebugMapObject &DMO); + + void findValidRelocsMachO(const object::SectionRef &Section, + const object::MachOObjectFile &Obj, + const DebugMapObject &DMO); + /// @} + + bool hasValidRelocation(uint32_t StartOffset, uint32_t EndOffset, + CompileUnit::DIEInfo &Info); + + bool applyValidRelocs(MutableArrayRef<char> Data, uint32_t BaseOffset, + bool isLittleEndian); + }; /// \defgroup FindRootDIEs Find DIEs corresponding to debug map entries. /// /// @{ /// \brief Recursively walk the \p DIE tree and look for DIEs to /// keep. Store that information in \p CU's DIEInfo. - void lookForDIEsToKeep(const DWARFDebugInfoEntryMinimal &DIE, + void lookForDIEsToKeep(RelocationManager &RelocMgr, + const DWARFDebugInfoEntryMinimal &DIE, const DebugMapObject &DMO, CompileUnit &CU, unsigned Flags); + /// If this compile unit is really a skeleton CU that points to a + /// clang module, register it in ClangModules and return true. + /// + /// A skeleton CU is a CU without children, a DW_AT_gnu_dwo_name + /// pointing to the module, and a DW_AT_gnu_dwo_id with the module + /// hash. + bool registerModuleReference(const DWARFDebugInfoEntryMinimal &CUDie, + const DWARFUnit &Unit, DebugMap &ModuleMap, + unsigned Indent = 0); + + /// Recursively add the debug info in this clang module .pcm + /// file (and all the modules imported by it in a bottom-up fashion) + /// to Units. + void loadClangModule(StringRef Filename, StringRef ModulePath, + StringRef ModuleName, uint64_t DwoId, + DebugMap &ModuleMap, unsigned Indent = 0); + /// \brief Flags passed to DwarfLinker::lookForDIEsToKeep enum TravesalFlags { TF_Keep = 1 << 0, ///< Mark the traversed DIEs as kept. TF_InFunctionScope = 1 << 1, ///< Current scope is a fucntion scope. TF_DependencyWalk = 1 << 2, ///< Walking the dependencies of a kept DIE. TF_ParentWalk = 1 << 3, ///< Walking up the parents of a kept DIE. + TF_ODR = 1 << 4, ///< Use the ODR whhile keeping dependants. + TF_SkipPC = 1 << 5, ///< Skip all location attributes. }; /// \brief Mark the passed DIE as well as all the ones it depends on /// as kept. - void keepDIEAndDenpendencies(const DWARFDebugInfoEntryMinimal &DIE, + void keepDIEAndDependencies(RelocationManager &RelocMgr, + const DWARFDebugInfoEntryMinimal &DIE, CompileUnit::DIEInfo &MyInfo, const DebugMapObject &DMO, CompileUnit &CU, - unsigned Flags); + bool UseODR); - unsigned shouldKeepDIE(const DWARFDebugInfoEntryMinimal &DIE, + unsigned shouldKeepDIE(RelocationManager &RelocMgr, + const DWARFDebugInfoEntryMinimal &DIE, CompileUnit &Unit, CompileUnit::DIEInfo &MyInfo, unsigned Flags); - unsigned shouldKeepVariableDIE(const DWARFDebugInfoEntryMinimal &DIE, + unsigned shouldKeepVariableDIE(RelocationManager &RelocMgr, + const DWARFDebugInfoEntryMinimal &DIE, CompileUnit &Unit, CompileUnit::DIEInfo &MyInfo, unsigned Flags); - unsigned shouldKeepSubprogramDIE(const DWARFDebugInfoEntryMinimal &DIE, + unsigned shouldKeepSubprogramDIE(RelocationManager &RelocMgr, + const DWARFDebugInfoEntryMinimal &DIE, CompileUnit &Unit, CompileUnit::DIEInfo &MyInfo, unsigned Flags); @@ -1134,76 +1257,118 @@ private: /// \defgroup Linking Methods used to link the debug information /// /// @{ - /// \brief Recursively clone \p InputDIE into an tree of DIE objects - /// where useless (as decided by lookForDIEsToKeep()) bits have been - /// stripped out and addresses have been rewritten according to the - /// debug map. - /// - /// \param OutOffset is the offset the cloned DIE in the output - /// compile unit. - /// \param PCOffset (while cloning a function scope) is the offset - /// applied to the entry point of the function to get the linked address. - /// - /// \returns the root of the cloned tree. - DIE *cloneDIE(const DWARFDebugInfoEntryMinimal &InputDIE, CompileUnit &U, - int64_t PCOffset, uint32_t OutOffset); - typedef DWARFAbbreviationDeclaration::AttributeSpec AttributeSpec; + class DIECloner { + DwarfLinker &Linker; + RelocationManager &RelocMgr; + /// Allocator used for all the DIEValue objects. + BumpPtrAllocator &DIEAlloc; + MutableArrayRef<CompileUnit> CompileUnits; + LinkOptions Options; - /// \brief Information gathered and exchanged between the various - /// clone*Attributes helpers about the attributes of a particular DIE. - struct AttributesInfo { - const char *Name, *MangledName; ///< Names. - uint32_t NameOffset, MangledNameOffset; ///< Offsets in the string pool. + public: + DIECloner(DwarfLinker &Linker, RelocationManager &RelocMgr, + BumpPtrAllocator &DIEAlloc, + MutableArrayRef<CompileUnit> CompileUnits, LinkOptions &Options) + : Linker(Linker), RelocMgr(RelocMgr), DIEAlloc(DIEAlloc), + CompileUnits(CompileUnits), Options(Options) {} - uint64_t OrigHighPc; ///< Value of AT_high_pc in the input DIE - int64_t PCOffset; ///< Offset to apply to PC addresses inside a function. + /// Recursively clone \p InputDIE into an tree of DIE objects + /// where useless (as decided by lookForDIEsToKeep()) bits have been + /// stripped out and addresses have been rewritten according to the + /// debug map. + /// + /// \param OutOffset is the offset the cloned DIE in the output + /// compile unit. + /// \param PCOffset (while cloning a function scope) is the offset + /// applied to the entry point of the function to get the linked address. + /// + /// \returns the root of the cloned tree or null if nothing was selected. + DIE *cloneDIE(const DWARFDebugInfoEntryMinimal &InputDIE, CompileUnit &U, + int64_t PCOffset, uint32_t OutOffset, unsigned Flags); - bool HasLowPc; ///< Does the DIE have a low_pc attribute? - bool IsDeclaration; ///< Is this DIE only a declaration? + /// Construct the output DIE tree by cloning the DIEs we + /// chose to keep above. If there are no valid relocs, then there's + /// nothing to clone/emit. + void cloneAllCompileUnits(DWARFContextInMemory &DwarfContext); - AttributesInfo() - : Name(nullptr), MangledName(nullptr), NameOffset(0), - MangledNameOffset(0), OrigHighPc(0), PCOffset(0), HasLowPc(false), - IsDeclaration(false) {} - }; + private: + typedef DWARFAbbreviationDeclaration::AttributeSpec AttributeSpec; + + /// Information gathered and exchanged between the various + /// clone*Attributes helpers about the attributes of a particular DIE. + struct AttributesInfo { + const char *Name, *MangledName; ///< Names. + uint32_t NameOffset, MangledNameOffset; ///< Offsets in the string pool. + + uint64_t OrigLowPc; ///< Value of AT_low_pc in the input DIE + uint64_t OrigHighPc; ///< Value of AT_high_pc in the input DIE + int64_t PCOffset; ///< Offset to apply to PC addresses inside a function. + + bool HasLowPc; ///< Does the DIE have a low_pc attribute? + bool IsDeclaration; ///< Is this DIE only a declaration? + + AttributesInfo() + : Name(nullptr), MangledName(nullptr), NameOffset(0), + MangledNameOffset(0), OrigLowPc(UINT64_MAX), OrigHighPc(0), + PCOffset(0), HasLowPc(false), IsDeclaration(false) {} + }; - /// \brief Helper for cloneDIE. - unsigned cloneAttribute(DIE &Die, const DWARFDebugInfoEntryMinimal &InputDIE, - CompileUnit &U, const DWARFFormValue &Val, - const AttributeSpec AttrSpec, unsigned AttrSize, - AttributesInfo &AttrInfo); + /// Helper for cloneDIE. + unsigned cloneAttribute(DIE &Die, + const DWARFDebugInfoEntryMinimal &InputDIE, + CompileUnit &U, const DWARFFormValue &Val, + const AttributeSpec AttrSpec, unsigned AttrSize, + AttributesInfo &AttrInfo); - /// \brief Helper for cloneDIE. - unsigned cloneStringAttribute(DIE &Die, AttributeSpec AttrSpec, - const DWARFFormValue &Val, const DWARFUnit &U); + /// Clone a string attribute described by \p AttrSpec and add + /// it to \p Die. + /// \returns the size of the new attribute. + unsigned cloneStringAttribute(DIE &Die, AttributeSpec AttrSpec, + const DWARFFormValue &Val, + const DWARFUnit &U); - /// \brief Helper for cloneDIE. - unsigned - cloneDieReferenceAttribute(DIE &Die, - const DWARFDebugInfoEntryMinimal &InputDIE, - AttributeSpec AttrSpec, unsigned AttrSize, - const DWARFFormValue &Val, CompileUnit &Unit); + /// Clone an attribute referencing another DIE and add + /// it to \p Die. + /// \returns the size of the new attribute. + unsigned + cloneDieReferenceAttribute(DIE &Die, + const DWARFDebugInfoEntryMinimal &InputDIE, + AttributeSpec AttrSpec, unsigned AttrSize, + const DWARFFormValue &Val, CompileUnit &Unit); - /// \brief Helper for cloneDIE. - unsigned cloneBlockAttribute(DIE &Die, AttributeSpec AttrSpec, - const DWARFFormValue &Val, unsigned AttrSize); + /// Clone an attribute referencing another DIE and add + /// it to \p Die. + /// \returns the size of the new attribute. + unsigned cloneBlockAttribute(DIE &Die, AttributeSpec AttrSpec, + const DWARFFormValue &Val, unsigned AttrSize); - /// \brief Helper for cloneDIE. - unsigned cloneAddressAttribute(DIE &Die, AttributeSpec AttrSpec, - const DWARFFormValue &Val, - const CompileUnit &Unit, AttributesInfo &Info); + /// Clone an attribute referencing another DIE and add + /// it to \p Die. + /// \returns the size of the new attribute. + unsigned cloneAddressAttribute(DIE &Die, AttributeSpec AttrSpec, + const DWARFFormValue &Val, + const CompileUnit &Unit, + AttributesInfo &Info); - /// \brief Helper for cloneDIE. - unsigned cloneScalarAttribute(DIE &Die, - const DWARFDebugInfoEntryMinimal &InputDIE, - CompileUnit &U, AttributeSpec AttrSpec, - const DWARFFormValue &Val, unsigned AttrSize, - AttributesInfo &Info); + /// Clone a scalar attribute and add it to \p Die. + /// \returns the size of the new attribute. + unsigned cloneScalarAttribute(DIE &Die, + const DWARFDebugInfoEntryMinimal &InputDIE, + CompileUnit &U, AttributeSpec AttrSpec, + const DWARFFormValue &Val, unsigned AttrSize, + AttributesInfo &Info); - /// \brief Helper for cloneDIE. - bool applyValidRelocs(MutableArrayRef<char> Data, uint32_t BaseOffset, - bool isLittleEndian); + /// Get the potential name and mangled name for the entity + /// described by \p Die and store them in \Info if they are not + /// already there. + /// \returns is a name was found. + bool getDIENames(const DWARFDebugInfoEntryMinimal &Die, DWARFUnit &U, + AttributesInfo &Info); + + /// Create a copy of abbreviation Abbrev. + void copyAbbrev(const DWARFAbbreviationDeclaration &Abbrev, bool hasODR); + }; /// \brief Assign an abbreviation number to \p Abbrev void AssignAbbrev(DIEAbbrev &Abbrev); @@ -1213,7 +1378,7 @@ private: /// \brief Storage for the unique Abbreviations. /// This is passed to AsmPrinter::emitDwarfAbbrevs(), thus it cannot /// be changed to a vecot of unique_ptrs. - std::vector<DIEAbbrev *> Abbreviations; + std::vector<std::unique_ptr<DIEAbbrev>> Abbreviations; /// \brief Compute and emit debug_ranges section for \p Unit, and /// patch the attributes referencing it. @@ -1243,35 +1408,31 @@ private: BumpPtrAllocator DIEAlloc; /// @} + /// ODR Contexts for that link. + DeclContextTree ODRContexts; + /// \defgroup Helpers Various helper methods. /// /// @{ - const DWARFDebugInfoEntryMinimal * - resolveDIEReference(DWARFFormValue &RefValue, const DWARFUnit &Unit, - const DWARFDebugInfoEntryMinimal &DIE, - CompileUnit *&ReferencedCU); - - CompileUnit *getUnitForOffset(unsigned Offset); - - bool getDIENames(const DWARFDebugInfoEntryMinimal &Die, DWARFUnit &U, - AttributesInfo &Info); - - void reportWarning(const Twine &Warning, const DWARFUnit *Unit = nullptr, - const DWARFDebugInfoEntryMinimal *DIE = nullptr) const; - bool createStreamer(Triple TheTriple, StringRef OutputFilename); + + /// \brief Attempt to load a debug object from disk. + ErrorOr<const object::ObjectFile &> loadObject(BinaryHolder &BinaryHolder, + DebugMapObject &Obj, + const DebugMap &Map); /// @} -private: std::string OutputFilename; LinkOptions Options; BinaryHolder BinHolder; std::unique_ptr<DwarfStreamer> Streamer; + uint64_t OutputDebugInfoSize; + unsigned UnitID; ///< A unique ID that identifies each compile unit. /// The units of the current debug map object. std::vector<CompileUnit> Units; - /// The debug map object curently under consideration. + /// The debug map object currently under consideration. DebugMapObject *CurrentDebugObject; /// \brief The Dwarf string pool @@ -1294,11 +1455,15 @@ private: /// Offset of the last CIE that has been emitted in the output /// debug_frame section. uint32_t LastCIEOffset; + + /// Mapping the PCM filename to the DwoId. + StringMap<uint64_t> ClangModules; }; -/// \brief Similar to DWARFUnitSection::getUnitForOffset(), but -/// returning our CompileUnit object instead. -CompileUnit *DwarfLinker::getUnitForOffset(unsigned Offset) { +/// Similar to DWARFUnitSection::getUnitForOffset(), but returning our +/// CompileUnit object instead. +static CompileUnit *getUnitForOffset(MutableArrayRef<CompileUnit> Units, + unsigned Offset) { auto CU = std::upper_bound(Units.begin(), Units.end(), Offset, [](uint32_t LHS, const CompileUnit &RHS) { @@ -1307,38 +1472,251 @@ CompileUnit *DwarfLinker::getUnitForOffset(unsigned Offset) { return CU != Units.end() ? &*CU : nullptr; } -/// \brief Resolve the DIE attribute reference that has been +/// Resolve the DIE attribute reference that has been /// extracted in \p RefValue. The resulting DIE migh be in another /// CompileUnit which is stored into \p ReferencedCU. /// \returns null if resolving fails for any reason. -const DWARFDebugInfoEntryMinimal *DwarfLinker::resolveDIEReference( - DWARFFormValue &RefValue, const DWARFUnit &Unit, +static const DWARFDebugInfoEntryMinimal *resolveDIEReference( + const DwarfLinker &Linker, MutableArrayRef<CompileUnit> Units, + const DWARFFormValue &RefValue, const DWARFUnit &Unit, const DWARFDebugInfoEntryMinimal &DIE, CompileUnit *&RefCU) { assert(RefValue.isFormClass(DWARFFormValue::FC_Reference)); uint64_t RefOffset = *RefValue.getAsReference(&Unit); - if ((RefCU = getUnitForOffset(RefOffset))) + if ((RefCU = getUnitForOffset(Units, RefOffset))) if (const auto *RefDie = RefCU->getOrigUnit().getDIEForOffset(RefOffset)) return RefDie; - reportWarning("could not find referenced DIE", &Unit, &DIE); + Linker.reportWarning("could not find referenced DIE", &Unit, &DIE); return nullptr; } -/// \brief Get the potential name and mangled name for the entity -/// described by \p Die and store them in \Info if they are not -/// already there. -/// \returns is a name was found. -bool DwarfLinker::getDIENames(const DWARFDebugInfoEntryMinimal &Die, - DWARFUnit &U, AttributesInfo &Info) { - // FIXME: a bit wastefull as the first getName might return the +/// \returns whether the passed \a Attr type might contain a DIE +/// reference suitable for ODR uniquing. +static bool isODRAttribute(uint16_t Attr) { + switch (Attr) { + default: + return false; + case dwarf::DW_AT_type: + case dwarf::DW_AT_containing_type: + case dwarf::DW_AT_specification: + case dwarf::DW_AT_abstract_origin: + case dwarf::DW_AT_import: + return true; + } + llvm_unreachable("Improper attribute."); +} + +/// Set the last DIE/CU a context was seen in and, possibly invalidate +/// the context if it is ambiguous. +/// +/// In the current implementation, we don't handle overloaded +/// functions well, because the argument types are not taken into +/// account when computing the DeclContext tree. +/// +/// Some of this is mitigated byt using mangled names that do contain +/// the arguments types, but sometimes (eg. with function templates) +/// we don't have that. In that case, just do not unique anything that +/// refers to the contexts we are not able to distinguish. +/// +/// If a context that is not a namespace appears twice in the same CU, +/// we know it is ambiguous. Make it invalid. +bool DeclContext::setLastSeenDIE(CompileUnit &U, + const DWARFDebugInfoEntryMinimal *Die) { + if (LastSeenCompileUnitID == U.getUniqueID()) { + DWARFUnit &OrigUnit = U.getOrigUnit(); + uint32_t FirstIdx = OrigUnit.getDIEIndex(LastSeenDIE); + U.getInfo(FirstIdx).Ctxt = nullptr; + return false; + } + + LastSeenCompileUnitID = U.getUniqueID(); + LastSeenDIE = Die; + return true; +} + +PointerIntPair<DeclContext *, 1> DeclContextTree::getChildDeclContext( + DeclContext &Context, const DWARFDebugInfoEntryMinimal *DIE, CompileUnit &U, + NonRelocatableStringpool &StringPool, bool InClangModule) { + unsigned Tag = DIE->getTag(); + + // FIXME: dsymutil-classic compat: We should bail out here if we + // have a specification or an abstract_origin. We will get the + // parent context wrong here. + + switch (Tag) { + default: + // By default stop gathering child contexts. + return PointerIntPair<DeclContext *, 1>(nullptr); + case dwarf::DW_TAG_module: + break; + case dwarf::DW_TAG_compile_unit: + return PointerIntPair<DeclContext *, 1>(&Context); + case dwarf::DW_TAG_subprogram: + // Do not unique anything inside CU local functions. + if ((Context.getTag() == dwarf::DW_TAG_namespace || + Context.getTag() == dwarf::DW_TAG_compile_unit) && + !DIE->getAttributeValueAsUnsignedConstant(&U.getOrigUnit(), + dwarf::DW_AT_external, 0)) + return PointerIntPair<DeclContext *, 1>(nullptr); + // Fallthrough + case dwarf::DW_TAG_member: + case dwarf::DW_TAG_namespace: + case dwarf::DW_TAG_structure_type: + case dwarf::DW_TAG_class_type: + case dwarf::DW_TAG_union_type: + case dwarf::DW_TAG_enumeration_type: + case dwarf::DW_TAG_typedef: + // Artificial things might be ambiguous, because they might be + // created on demand. For example implicitely defined constructors + // are ambiguous because of the way we identify contexts, and they + // won't be generated everytime everywhere. + if (DIE->getAttributeValueAsUnsignedConstant(&U.getOrigUnit(), + dwarf::DW_AT_artificial, 0)) + return PointerIntPair<DeclContext *, 1>(nullptr); + break; + } + + const char *Name = DIE->getName(&U.getOrigUnit(), DINameKind::LinkageName); + const char *ShortName = DIE->getName(&U.getOrigUnit(), DINameKind::ShortName); + StringRef NameRef; + StringRef ShortNameRef; + StringRef FileRef; + + if (Name) + NameRef = StringPool.internString(Name); + else if (Tag == dwarf::DW_TAG_namespace) + // FIXME: For dsymutil-classic compatibility. I think uniquing + // within anonymous namespaces is wrong. There is no ODR guarantee + // there. + NameRef = StringPool.internString("(anonymous namespace)"); + + if (ShortName && ShortName != Name) + ShortNameRef = StringPool.internString(ShortName); + else + ShortNameRef = NameRef; + + if (Tag != dwarf::DW_TAG_class_type && Tag != dwarf::DW_TAG_structure_type && + Tag != dwarf::DW_TAG_union_type && + Tag != dwarf::DW_TAG_enumeration_type && NameRef.empty()) + return PointerIntPair<DeclContext *, 1>(nullptr); + + std::string File; + unsigned Line = 0; + unsigned ByteSize = UINT32_MAX; + + if (!InClangModule) { + // Gather some discriminating data about the DeclContext we will be + // creating: File, line number and byte size. This shouldn't be + // necessary, because the ODR is just about names, but given that we + // do some approximations with overloaded functions and anonymous + // namespaces, use these additional data points to make the process + // safer. This is disabled for clang modules, because forward + // declarations of module-defined types do not have a file and line. + ByteSize = DIE->getAttributeValueAsUnsignedConstant( + &U.getOrigUnit(), dwarf::DW_AT_byte_size, UINT64_MAX); + if (Tag != dwarf::DW_TAG_namespace || !Name) { + if (unsigned FileNum = DIE->getAttributeValueAsUnsignedConstant( + &U.getOrigUnit(), dwarf::DW_AT_decl_file, 0)) { + if (const auto *LT = U.getOrigUnit().getContext().getLineTableForUnit( + &U.getOrigUnit())) { + // FIXME: dsymutil-classic compatibility. I'd rather not + // unique anything in anonymous namespaces, but if we do, then + // verify that the file and line correspond. + if (!Name && Tag == dwarf::DW_TAG_namespace) + FileNum = 1; + + // FIXME: Passing U.getOrigUnit().getCompilationDir() + // instead of "" would allow more uniquing, but for now, do + // it this way to match dsymutil-classic. + if (LT->getFileNameByIndex( + FileNum, "", + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, + File)) { + Line = DIE->getAttributeValueAsUnsignedConstant( + &U.getOrigUnit(), dwarf::DW_AT_decl_line, 0); +#ifdef HAVE_REALPATH + // Cache the resolved paths, because calling realpath is expansive. + if (const char *ResolvedPath = U.getResolvedPath(FileNum)) { + File = ResolvedPath; + } else { + char RealPath[PATH_MAX + 1]; + RealPath[PATH_MAX] = 0; + if (::realpath(File.c_str(), RealPath)) + File = RealPath; + U.setResolvedPath(FileNum, File); + } +#endif + FileRef = StringPool.internString(File); + } + } + } + } + } + + if (!Line && NameRef.empty()) + return PointerIntPair<DeclContext *, 1>(nullptr); + + // We hash NameRef, which is the mangled name, in order to get most + // overloaded functions resolve correctly. + // + // Strictly speaking, hashing the Tag is only necessary for a + // DW_TAG_module, to prevent uniquing of a module and a namespace + // with the same name. + // + // FIXME: dsymutil-classic won't unique the same type presented + // once as a struct and once as a class. Using the Tag in the fully + // qualified name hash to get the same effect. + unsigned Hash = hash_combine(Context.getQualifiedNameHash(), Tag, NameRef); + + // FIXME: dsymutil-classic compatibility: when we don't have a name, + // use the filename. + if (Tag == dwarf::DW_TAG_namespace && NameRef == "(anonymous namespace)") + Hash = hash_combine(Hash, FileRef); + + // Now look if this context already exists. + DeclContext Key(Hash, Line, ByteSize, Tag, NameRef, FileRef, Context); + auto ContextIter = Contexts.find(&Key); + + if (ContextIter == Contexts.end()) { + // The context wasn't found. + bool Inserted; + DeclContext *NewContext = + new (Allocator) DeclContext(Hash, Line, ByteSize, Tag, NameRef, FileRef, + Context, DIE, U.getUniqueID()); + std::tie(ContextIter, Inserted) = Contexts.insert(NewContext); + assert(Inserted && "Failed to insert DeclContext"); + (void)Inserted; + } else if (Tag != dwarf::DW_TAG_namespace && + !(*ContextIter)->setLastSeenDIE(U, DIE)) { + // The context was found, but it is ambiguous with another context + // in the same file. Mark it invalid. + return PointerIntPair<DeclContext *, 1>(*ContextIter, /* Invalid= */ 1); + } + + assert(ContextIter != Contexts.end()); + // FIXME: dsymutil-classic compatibility. Union types aren't + // uniques, but their children might be. + if ((Tag == dwarf::DW_TAG_subprogram && + Context.getTag() != dwarf::DW_TAG_structure_type && + Context.getTag() != dwarf::DW_TAG_class_type) || + (Tag == dwarf::DW_TAG_union_type)) + return PointerIntPair<DeclContext *, 1>(*ContextIter, /* Invalid= */ 1); + + return PointerIntPair<DeclContext *, 1>(*ContextIter); +} + +bool DwarfLinker::DIECloner::getDIENames(const DWARFDebugInfoEntryMinimal &Die, + DWARFUnit &U, AttributesInfo &Info) { + // FIXME: a bit wasteful as the first getName might return the // short name. if (!Info.MangledName && (Info.MangledName = Die.getName(&U, DINameKind::LinkageName))) - Info.MangledNameOffset = StringPool.getStringOffset(Info.MangledName); + Info.MangledNameOffset = + Linker.StringPool.getStringOffset(Info.MangledName); if (!Info.Name && (Info.Name = Die.getName(&U, DINameKind::ShortName))) - Info.NameOffset = StringPool.getStringOffset(Info.Name); + Info.NameOffset = Linker.StringPool.getStringOffset(Info.Name); return Info.Name || Info.MangledName; } @@ -1368,17 +1746,70 @@ bool DwarfLinker::createStreamer(Triple TheTriple, StringRef OutputFilename) { return Streamer->init(TheTriple, OutputFilename); } -/// \brief Recursive helper to gather the child->parent relationships in the -/// original compile unit. -static void gatherDIEParents(const DWARFDebugInfoEntryMinimal *DIE, - unsigned ParentIdx, CompileUnit &CU) { +/// Recursive helper to build the global DeclContext information and +/// gather the child->parent relationships in the original compile unit. +/// +/// \return true when this DIE and all of its children are only +/// forward declarations to types defined in external clang modules +/// (i.e., forward declarations that are children of a DW_TAG_module). +static bool analyzeContextInfo(const DWARFDebugInfoEntryMinimal *DIE, + unsigned ParentIdx, CompileUnit &CU, + DeclContext *CurrentDeclContext, + NonRelocatableStringpool &StringPool, + DeclContextTree &Contexts, + bool InImportedModule = false) { unsigned MyIdx = CU.getOrigUnit().getDIEIndex(DIE); - CU.getInfo(MyIdx).ParentIdx = ParentIdx; + CompileUnit::DIEInfo &Info = CU.getInfo(MyIdx); + + // Clang imposes an ODR on modules(!) regardless of the language: + // "The module-id should consist of only a single identifier, + // which provides the name of the module being defined. Each + // module shall have a single definition." + // + // This does not extend to the types inside the modules: + // "[I]n C, this implies that if two structs are defined in + // different submodules with the same name, those two types are + // distinct types (but may be compatible types if their + // definitions match)." + // + // We treat non-C++ modules like namespaces for this reason. + if (DIE->getTag() == dwarf::DW_TAG_module && ParentIdx == 0 && + DIE->getAttributeValueAsString(&CU.getOrigUnit(), dwarf::DW_AT_name, + "") != CU.getClangModuleName()) { + InImportedModule = true; + } + Info.ParentIdx = ParentIdx; + bool InClangModule = CU.isClangModule() || InImportedModule; + if (CU.hasODR() || InClangModule) { + if (CurrentDeclContext) { + auto PtrInvalidPair = Contexts.getChildDeclContext( + *CurrentDeclContext, DIE, CU, StringPool, InClangModule); + CurrentDeclContext = PtrInvalidPair.getPointer(); + Info.Ctxt = + PtrInvalidPair.getInt() ? nullptr : PtrInvalidPair.getPointer(); + } else + Info.Ctxt = CurrentDeclContext = nullptr; + } + + Info.Prune = InImportedModule; if (DIE->hasChildren()) for (auto *Child = DIE->getFirstChild(); Child && !Child->isNULL(); Child = Child->getSibling()) - gatherDIEParents(Child, MyIdx, CU); + Info.Prune &= analyzeContextInfo(Child, MyIdx, CU, CurrentDeclContext, + StringPool, Contexts, InImportedModule); + + // Prune this DIE if it is either a forward declaration inside a + // DW_TAG_module or a DW_TAG_module that contains nothing but + // forward declarations. + Info.Prune &= (DIE->getTag() == dwarf::DW_TAG_module) || + DIE->getAttributeValueAsUnsignedConstant( + &CU.getOrigUnit(), dwarf::DW_AT_declaration, 0); + + // Don't prune it if there is no definition for the DIE. + Info.Prune &= Info.Ctxt && Info.Ctxt->getCanonicalDIEOffset(); + + return Info.Prune; } static bool dieNeedsChildrenToBeMeaningful(uint32_t Tag) { @@ -1396,9 +1827,14 @@ static bool dieNeedsChildrenToBeMeaningful(uint32_t Tag) { llvm_unreachable("Invalid Tag"); } +static unsigned getRefAddrSize(const DWARFUnit &U) { + if (U.getVersion() == 2) + return U.getAddressByteSize(); + return 4; +} + void DwarfLinker::startDebugObject(DWARFContext &Dwarf, DebugMapObject &Obj) { Units.reserve(Dwarf.getNumCompileUnits()); - NextValidReloc = 0; // Iterate over the debug map entries and put all the ones that are // functions (because they have a size) into the Ranges map. This // map is very similar to the FunctionRanges that are stored in each @@ -1424,7 +1860,6 @@ void DwarfLinker::startDebugObject(DWARFContext &Dwarf, DebugMapObject &Obj) { void DwarfLinker::endDebugObject() { Units.clear(); - ValidRelocs.clear(); Ranges.clear(); for (auto I = DIEBlocks.begin(), E = DIEBlocks.end(); I != E; ++I) @@ -1440,9 +1875,10 @@ void DwarfLinker::endDebugObject() { /// \brief Iterate over the relocations of the given \p Section and /// store the ones that correspond to debug map entries into the /// ValidRelocs array. -void DwarfLinker::findValidRelocsMachO(const object::SectionRef &Section, - const object::MachOObjectFile &Obj, - const DebugMapObject &DMO) { +void DwarfLinker::RelocationManager:: +findValidRelocsMachO(const object::SectionRef &Section, + const object::MachOObjectFile &Obj, + const DebugMapObject &DMO) { StringRef Contents; Section.getContents(Contents); DataExtractor Data(Contents, Obj.isLittleEndian(), 0); @@ -1453,7 +1889,7 @@ void DwarfLinker::findValidRelocsMachO(const object::SectionRef &Section, unsigned RelocSize = 1 << Obj.getAnyRelocationLength(MachOReloc); uint64_t Offset64 = Reloc.getOffset(); if ((RelocSize != 4 && RelocSize != 8)) { - reportWarning(" unsupported relocation in debug_info section."); + Linker.reportWarning(" unsupported relocation in debug_info section."); continue; } uint32_t Offset = Offset64; @@ -1464,7 +1900,7 @@ void DwarfLinker::findValidRelocsMachO(const object::SectionRef &Section, if (Sym != Obj.symbol_end()) { ErrorOr<StringRef> SymbolName = Sym->getName(); if (!SymbolName) { - reportWarning("error getting relocation symbol name."); + Linker.reportWarning("error getting relocation symbol name."); continue; } if (const auto *Mapping = DMO.lookupSymbol(*SymbolName)) @@ -1480,14 +1916,15 @@ void DwarfLinker::findValidRelocsMachO(const object::SectionRef &Section, /// \brief Dispatch the valid relocation finding logic to the /// appropriate handler depending on the object file format. -bool DwarfLinker::findValidRelocs(const object::SectionRef &Section, - const object::ObjectFile &Obj, - const DebugMapObject &DMO) { +bool DwarfLinker::RelocationManager::findValidRelocs( + const object::SectionRef &Section, const object::ObjectFile &Obj, + const DebugMapObject &DMO) { // Dispatch to the right handler depending on the file type. if (auto *MachOObj = dyn_cast<object::MachOObjectFile>(&Obj)) findValidRelocsMachO(Section, *MachOObj, DMO); else - reportWarning(Twine("unsupported object file type: ") + Obj.getFileName()); + Linker.reportWarning(Twine("unsupported object file type: ") + + Obj.getFileName()); if (ValidRelocs.empty()) return false; @@ -1505,8 +1942,9 @@ bool DwarfLinker::findValidRelocs(const object::SectionRef &Section, /// link by indicating which DIEs refer to symbols present in the /// linked binary. /// \returns wether there are any valid relocations in the debug info. -bool DwarfLinker::findValidRelocsInDebugInfo(const object::ObjectFile &Obj, - const DebugMapObject &DMO) { +bool DwarfLinker::RelocationManager:: +findValidRelocsInDebugInfo(const object::ObjectFile &Obj, + const DebugMapObject &DMO) { // Find the debug_info section. for (const object::SectionRef &Section : Obj.sections()) { StringRef SectionName; @@ -1525,8 +1963,9 @@ bool DwarfLinker::findValidRelocsInDebugInfo(const object::ObjectFile &Obj, /// This function must be called with offsets in strictly ascending /// order because it never looks back at relocations it already 'went past'. /// \returns true and sets Info.InDebugMap if it is the case. -bool DwarfLinker::hasValidRelocation(uint32_t StartOffset, uint32_t EndOffset, - CompileUnit::DIEInfo &Info) { +bool DwarfLinker::RelocationManager:: +hasValidRelocation(uint32_t StartOffset, uint32_t EndOffset, + CompileUnit::DIEInfo &Info) { assert(NextValidReloc == 0 || StartOffset > ValidRelocs[NextValidReloc - 1].Offset); if (NextValidReloc >= ValidRelocs.size()) @@ -1546,7 +1985,7 @@ bool DwarfLinker::hasValidRelocation(uint32_t StartOffset, uint32_t EndOffset, const auto &ValidReloc = ValidRelocs[NextValidReloc++]; const auto &Mapping = ValidReloc.Mapping->getValue(); - if (Options.Verbose) + if (Linker.Options.Verbose) outs() << "Found valid debug map entry: " << ValidReloc.Mapping->getKey() << " " << format("\t%016" PRIx64 " => %016" PRIx64, uint64_t(Mapping.ObjectAddress), @@ -1579,9 +2018,11 @@ getAttributeOffsets(const DWARFAbbreviationDeclaration *Abbrev, unsigned Idx, /// \brief Check if a variable describing DIE should be kept. /// \returns updated TraversalFlags. -unsigned DwarfLinker::shouldKeepVariableDIE( - const DWARFDebugInfoEntryMinimal &DIE, CompileUnit &Unit, - CompileUnit::DIEInfo &MyInfo, unsigned Flags) { +unsigned DwarfLinker::shouldKeepVariableDIE(RelocationManager &RelocMgr, + const DWARFDebugInfoEntryMinimal &DIE, + CompileUnit &Unit, + CompileUnit::DIEInfo &MyInfo, + unsigned Flags) { const auto *Abbrev = DIE.getAbbreviationDeclarationPtr(); // Global variables with constant value can always be kept. @@ -1606,7 +2047,7 @@ unsigned DwarfLinker::shouldKeepVariableDIE( // always check in the variable has a valid relocation, so that the // DIEInfo is filled. However, we don't want a static variable in a // function to force us to keep the enclosing function. - if (!hasValidRelocation(LocationOffset, LocationEndOffset, MyInfo) || + if (!RelocMgr.hasValidRelocation(LocationOffset, LocationEndOffset, MyInfo) || (Flags & TF_InFunctionScope)) return Flags; @@ -1619,6 +2060,7 @@ unsigned DwarfLinker::shouldKeepVariableDIE( /// \brief Check if a function describing DIE should be kept. /// \returns updated TraversalFlags. unsigned DwarfLinker::shouldKeepSubprogramDIE( + RelocationManager &RelocMgr, const DWARFDebugInfoEntryMinimal &DIE, CompileUnit &Unit, CompileUnit::DIEInfo &MyInfo, unsigned Flags) { const auto *Abbrev = DIE.getAbbreviationDeclarationPtr(); @@ -1639,7 +2081,7 @@ unsigned DwarfLinker::shouldKeepSubprogramDIE( DIE.getAttributeValueAsAddress(&OrigUnit, dwarf::DW_AT_low_pc, -1ULL); assert(LowPc != -1ULL && "low_pc attribute is not an address."); if (LowPc == -1ULL || - !hasValidRelocation(LowPcOffset, LowPcEndOffset, MyInfo)) + !RelocMgr.hasValidRelocation(LowPcOffset, LowPcEndOffset, MyInfo)) return Flags; if (Options.Verbose) @@ -1670,16 +2112,17 @@ unsigned DwarfLinker::shouldKeepSubprogramDIE( /// \brief Check if a DIE should be kept. /// \returns updated TraversalFlags. -unsigned DwarfLinker::shouldKeepDIE(const DWARFDebugInfoEntryMinimal &DIE, +unsigned DwarfLinker::shouldKeepDIE(RelocationManager &RelocMgr, + const DWARFDebugInfoEntryMinimal &DIE, CompileUnit &Unit, CompileUnit::DIEInfo &MyInfo, unsigned Flags) { switch (DIE.getTag()) { case dwarf::DW_TAG_constant: case dwarf::DW_TAG_variable: - return shouldKeepVariableDIE(DIE, Unit, MyInfo, Flags); + return shouldKeepVariableDIE(RelocMgr, DIE, Unit, MyInfo, Flags); case dwarf::DW_TAG_subprogram: - return shouldKeepSubprogramDIE(DIE, Unit, MyInfo, Flags); + return shouldKeepSubprogramDIE(RelocMgr, DIE, Unit, MyInfo, Flags); case dwarf::DW_TAG_module: case dwarf::DW_TAG_imported_module: case dwarf::DW_TAG_imported_declaration: @@ -1699,26 +2142,28 @@ unsigned DwarfLinker::shouldKeepDIE(const DWARFDebugInfoEntryMinimal &DIE, /// back to lookForDIEsToKeep while adding TF_DependencyWalk to the /// TraversalFlags to inform it that it's not doing the primary DIE /// tree walk. -void DwarfLinker::keepDIEAndDenpendencies(const DWARFDebugInfoEntryMinimal &DIE, +void DwarfLinker::keepDIEAndDependencies(RelocationManager &RelocMgr, + const DWARFDebugInfoEntryMinimal &Die, CompileUnit::DIEInfo &MyInfo, const DebugMapObject &DMO, - CompileUnit &CU, unsigned Flags) { + CompileUnit &CU, bool UseODR) { const DWARFUnit &Unit = CU.getOrigUnit(); MyInfo.Keep = true; // First mark all the parent chain as kept. unsigned AncestorIdx = MyInfo.ParentIdx; while (!CU.getInfo(AncestorIdx).Keep) { - lookForDIEsToKeep(*Unit.getDIEAtIndex(AncestorIdx), DMO, CU, - TF_ParentWalk | TF_Keep | TF_DependencyWalk); + unsigned ODRFlag = UseODR ? TF_ODR : 0; + lookForDIEsToKeep(RelocMgr, *Unit.getDIEAtIndex(AncestorIdx), DMO, CU, + TF_ParentWalk | TF_Keep | TF_DependencyWalk | ODRFlag); AncestorIdx = CU.getInfo(AncestorIdx).ParentIdx; } // Then we need to mark all the DIEs referenced by this DIE's // attributes as kept. DataExtractor Data = Unit.getDebugInfoExtractor(); - const auto *Abbrev = DIE.getAbbreviationDeclarationPtr(); - uint32_t Offset = DIE.getOffset() + getULEB128Size(Abbrev->getCode()); + const auto *Abbrev = Die.getAbbreviationDeclarationPtr(); + uint32_t Offset = Die.getOffset() + getULEB128Size(Abbrev->getCode()); // Mark all DIEs referenced through atttributes as kept. for (const auto &AttrSpec : Abbrev->attributes()) { @@ -1731,9 +2176,33 @@ void DwarfLinker::keepDIEAndDenpendencies(const DWARFDebugInfoEntryMinimal &DIE, Val.extractValue(Data, &Offset, &Unit); CompileUnit *ReferencedCU; - if (const auto *RefDIE = resolveDIEReference(Val, Unit, DIE, ReferencedCU)) - lookForDIEsToKeep(*RefDIE, DMO, *ReferencedCU, - TF_Keep | TF_DependencyWalk); + if (const auto *RefDIE = + resolveDIEReference(*this, MutableArrayRef<CompileUnit>(Units), Val, + Unit, Die, ReferencedCU)) { + uint32_t RefIdx = ReferencedCU->getOrigUnit().getDIEIndex(RefDIE); + CompileUnit::DIEInfo &Info = ReferencedCU->getInfo(RefIdx); + // If the referenced DIE has a DeclContext that has already been + // emitted, then do not keep the one in this CU. We'll link to + // the canonical DIE in cloneDieReferenceAttribute. + // FIXME: compatibility with dsymutil-classic. UseODR shouldn't + // be necessary and could be advantageously replaced by + // ReferencedCU->hasODR() && CU.hasODR(). + // FIXME: compatibility with dsymutil-classic. There is no + // reason not to unique ref_addr references. + if (AttrSpec.Form != dwarf::DW_FORM_ref_addr && UseODR && Info.Ctxt && + Info.Ctxt != ReferencedCU->getInfo(Info.ParentIdx).Ctxt && + Info.Ctxt->getCanonicalDIEOffset() && isODRAttribute(AttrSpec.Attr)) + continue; + + // Keep a module forward declaration if there is no definition. + if (!(isODRAttribute(AttrSpec.Attr) && Info.Ctxt && + Info.Ctxt->getCanonicalDIEOffset())) + Info.Prune = false; + + unsigned ODRFlag = UseODR ? TF_ODR : 0; + lookForDIEsToKeep(RelocMgr, *RefDIE, DMO, *ReferencedCU, + TF_Keep | TF_DependencyWalk | ODRFlag); + } } } @@ -1749,12 +2218,15 @@ void DwarfLinker::keepDIEAndDenpendencies(const DWARFDebugInfoEntryMinimal &DIE, /// also called, but during these dependency walks the file order is /// not respected. The TF_DependencyWalk flag tells us which kind of /// traversal we are currently doing. -void DwarfLinker::lookForDIEsToKeep(const DWARFDebugInfoEntryMinimal &DIE, +void DwarfLinker::lookForDIEsToKeep(RelocationManager &RelocMgr, + const DWARFDebugInfoEntryMinimal &Die, const DebugMapObject &DMO, CompileUnit &CU, unsigned Flags) { - unsigned Idx = CU.getOrigUnit().getDIEIndex(&DIE); + unsigned Idx = CU.getOrigUnit().getDIEIndex(&Die); CompileUnit::DIEInfo &MyInfo = CU.getInfo(Idx); bool AlreadyKept = MyInfo.Keep; + if (MyInfo.Prune) + return; // If the Keep flag is set, we are marking a required DIE's // dependencies. If our target is already marked as kept, we're all @@ -1762,30 +2234,31 @@ void DwarfLinker::lookForDIEsToKeep(const DWARFDebugInfoEntryMinimal &DIE, if ((Flags & TF_DependencyWalk) && AlreadyKept) return; - // We must not call shouldKeepDIE while called from keepDIEAndDenpendencies, + // We must not call shouldKeepDIE while called from keepDIEAndDependencies, // because it would screw up the relocation finding logic. if (!(Flags & TF_DependencyWalk)) - Flags = shouldKeepDIE(DIE, CU, MyInfo, Flags); + Flags = shouldKeepDIE(RelocMgr, Die, CU, MyInfo, Flags); // If it is a newly kept DIE mark it as well as all its dependencies as kept. - if (!AlreadyKept && (Flags & TF_Keep)) - keepDIEAndDenpendencies(DIE, MyInfo, DMO, CU, Flags); - + if (!AlreadyKept && (Flags & TF_Keep)) { + bool UseOdr = (Flags & TF_DependencyWalk) ? (Flags & TF_ODR) : CU.hasODR(); + keepDIEAndDependencies(RelocMgr, Die, MyInfo, DMO, CU, UseOdr); + } // The TF_ParentWalk flag tells us that we are currently walking up // the parent chain of a required DIE, and we don't want to mark all // the children of the parents as kept (consider for example a // DW_TAG_namespace node in the parent chain). There are however a // set of DIE types for which we want to ignore that directive and still // walk their children. - if (dieNeedsChildrenToBeMeaningful(DIE.getTag())) + if (dieNeedsChildrenToBeMeaningful(Die.getTag())) Flags &= ~TF_ParentWalk; - if (!DIE.hasChildren() || (Flags & TF_ParentWalk)) + if (!Die.hasChildren() || (Flags & TF_ParentWalk)) return; - for (auto *Child = DIE.getFirstChild(); Child && !Child->isNULL(); + for (auto *Child = Die.getFirstChild(); Child && !Child->isNULL(); Child = Child->getSibling()) - lookForDIEsToKeep(*Child, DMO, CU, Flags); + lookForDIEsToKeep(RelocMgr, *Child, DMO, CU, Flags); } /// \brief Assign an abbreviation numer to \p Abbrev. @@ -1808,55 +2281,60 @@ void DwarfLinker::AssignAbbrev(DIEAbbrev &Abbrev) { } else { // Add to abbreviation list. Abbreviations.push_back( - new DIEAbbrev(Abbrev.getTag(), Abbrev.hasChildren())); + llvm::make_unique<DIEAbbrev>(Abbrev.getTag(), Abbrev.hasChildren())); for (const auto &Attr : Abbrev.getData()) Abbreviations.back()->AddAttribute(Attr.getAttribute(), Attr.getForm()); - AbbreviationsSet.InsertNode(Abbreviations.back(), InsertToken); + AbbreviationsSet.InsertNode(Abbreviations.back().get(), InsertToken); // Assign the unique abbreviation number. Abbrev.setNumber(Abbreviations.size()); Abbreviations.back()->setNumber(Abbreviations.size()); } } -/// \brief Clone a string attribute described by \p AttrSpec and add -/// it to \p Die. -/// \returns the size of the new attribute. -unsigned DwarfLinker::cloneStringAttribute(DIE &Die, AttributeSpec AttrSpec, - const DWARFFormValue &Val, - const DWARFUnit &U) { +unsigned DwarfLinker::DIECloner::cloneStringAttribute(DIE &Die, + AttributeSpec AttrSpec, + const DWARFFormValue &Val, + const DWARFUnit &U) { // Switch everything to out of line strings. const char *String = *Val.getAsCString(&U); - unsigned Offset = StringPool.getStringOffset(String); + unsigned Offset = Linker.StringPool.getStringOffset(String); Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), dwarf::DW_FORM_strp, DIEInteger(Offset)); return 4; } -/// \brief Clone an attribute referencing another DIE and add -/// it to \p Die. -/// \returns the size of the new attribute. -unsigned DwarfLinker::cloneDieReferenceAttribute( +unsigned DwarfLinker::DIECloner::cloneDieReferenceAttribute( DIE &Die, const DWARFDebugInfoEntryMinimal &InputDIE, AttributeSpec AttrSpec, unsigned AttrSize, const DWARFFormValue &Val, CompileUnit &Unit) { - uint32_t Ref = *Val.getAsReference(&Unit.getOrigUnit()); + const DWARFUnit &U = Unit.getOrigUnit(); + uint32_t Ref = *Val.getAsReference(&U); DIE *NewRefDie = nullptr; CompileUnit *RefUnit = nullptr; - const DWARFDebugInfoEntryMinimal *RefDie = nullptr; + DeclContext *Ctxt = nullptr; - if (!(RefUnit = getUnitForOffset(Ref)) || - !(RefDie = RefUnit->getOrigUnit().getDIEForOffset(Ref))) { - const char *AttributeString = dwarf::AttributeString(AttrSpec.Attr); - if (!AttributeString) - AttributeString = "DW_AT_???"; - reportWarning(Twine("Missing DIE for ref in attribute ") + AttributeString + - ". Dropping.", - &Unit.getOrigUnit(), &InputDIE); + const DWARFDebugInfoEntryMinimal *RefDie = + resolveDIEReference(Linker, CompileUnits, Val, U, InputDIE, RefUnit); + + // If the referenced DIE is not found, drop the attribute. + if (!RefDie) return 0; - } unsigned Idx = RefUnit->getOrigUnit().getDIEIndex(RefDie); CompileUnit::DIEInfo &RefInfo = RefUnit->getInfo(Idx); + + // If we already have emitted an equivalent DeclContext, just point + // at it. + if (isODRAttribute(AttrSpec.Attr)) { + Ctxt = RefInfo.Ctxt; + if (Ctxt && Ctxt->getCanonicalDIEOffset()) { + DIEInteger Attr(Ctxt->getCanonicalDIEOffset()); + Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), + dwarf::DW_FORM_ref_addr, Attr); + return getRefAddrSize(U); + } + } + if (!RefInfo.Clone) { assert(Ref > InputDIE.getOffset()); // We haven't cloned this DIE yet. Just create an empty one and @@ -1865,7 +2343,8 @@ unsigned DwarfLinker::cloneDieReferenceAttribute( } NewRefDie = RefInfo.Clone; - if (AttrSpec.Form == dwarf::DW_FORM_ref_addr) { + if (AttrSpec.Form == dwarf::DW_FORM_ref_addr || + (Unit.hasODR() && isODRAttribute(AttrSpec.Attr))) { // We cannot currently rely on a DIEEntry to emit ref_addr // references, because the implementation calls back to DwarfDebug // to find the unit offset. (We don't have a DwarfDebug) @@ -1883,11 +2362,11 @@ unsigned DwarfLinker::cloneDieReferenceAttribute( // A forward reference. Note and fixup later. Attr = 0xBADDEF; Unit.noteForwardReference( - NewRefDie, RefUnit, + NewRefDie, RefUnit, Ctxt, Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), dwarf::DW_FORM_ref_addr, DIEInteger(Attr))); } - return AttrSize; + return getRefAddrSize(U); } Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), @@ -1895,25 +2374,24 @@ unsigned DwarfLinker::cloneDieReferenceAttribute( return AttrSize; } -/// \brief Clone an attribute of block form (locations, constants) and add -/// it to \p Die. -/// \returns the size of the new attribute. -unsigned DwarfLinker::cloneBlockAttribute(DIE &Die, AttributeSpec AttrSpec, - const DWARFFormValue &Val, - unsigned AttrSize) { - DIE *Attr; +unsigned DwarfLinker::DIECloner::cloneBlockAttribute(DIE &Die, + AttributeSpec AttrSpec, + const DWARFFormValue &Val, + unsigned AttrSize) { + DIEValueList *Attr; DIEValue Value; DIELoc *Loc = nullptr; DIEBlock *Block = nullptr; // Just copy the block data over. if (AttrSpec.Form == dwarf::DW_FORM_exprloc) { Loc = new (DIEAlloc) DIELoc; - DIELocs.push_back(Loc); + Linker.DIELocs.push_back(Loc); } else { Block = new (DIEAlloc) DIEBlock; - DIEBlocks.push_back(Block); + Linker.DIEBlocks.push_back(Block); } - Attr = Loc ? static_cast<DIE *>(Loc) : static_cast<DIE *>(Block); + Attr = Loc ? static_cast<DIEValueList *>(Loc) + : static_cast<DIEValueList *>(Block); if (Loc) Value = DIEValue(dwarf::Attribute(AttrSpec.Attr), @@ -1928,27 +2406,30 @@ unsigned DwarfLinker::cloneBlockAttribute(DIE &Die, AttributeSpec AttrSpec, // FIXME: If DIEBlock and DIELoc just reuses the Size field of // the DIE class, this if could be replaced by // Attr->setSize(Bytes.size()). - if (Streamer) { + if (Linker.Streamer) { + auto *AsmPrinter = &Linker.Streamer->getAsmPrinter(); if (Loc) - Loc->ComputeSize(&Streamer->getAsmPrinter()); + Loc->ComputeSize(AsmPrinter); else - Block->ComputeSize(&Streamer->getAsmPrinter()); + Block->ComputeSize(AsmPrinter); } Die.addValue(DIEAlloc, Value); return AttrSize; } -/// \brief Clone an address attribute and add it to \p Die. -/// \returns the size of the new attribute. -unsigned DwarfLinker::cloneAddressAttribute(DIE &Die, AttributeSpec AttrSpec, - const DWARFFormValue &Val, - const CompileUnit &Unit, - AttributesInfo &Info) { +unsigned DwarfLinker::DIECloner::cloneAddressAttribute( + DIE &Die, AttributeSpec AttrSpec, const DWARFFormValue &Val, + const CompileUnit &Unit, AttributesInfo &Info) { uint64_t Addr = *Val.getAsAddress(&Unit.getOrigUnit()); if (AttrSpec.Attr == dwarf::DW_AT_low_pc) { if (Die.getTag() == dwarf::DW_TAG_inlined_subroutine || Die.getTag() == dwarf::DW_TAG_lexical_block) - Addr += Info.PCOffset; + // The low_pc of a block or inline subroutine might get + // relocated because it happens to match the low_pc of the + // enclosing subprogram. To prevent issues with that, always use + // the low_pc from the input DIE if relocations have been applied. + Addr = (Info.OrigLowPc != UINT64_MAX ? Info.OrigLowPc : Addr) + + Info.PCOffset; else if (Die.getTag() == dwarf::DW_TAG_compile_unit) { Addr = Unit.getLowPc(); if (Addr == UINT64_MAX) @@ -1973,9 +2454,7 @@ unsigned DwarfLinker::cloneAddressAttribute(DIE &Die, AttributeSpec AttrSpec, return Unit.getOrigUnit().getAddressByteSize(); } -/// \brief Clone a scalar attribute and add it to \p Die. -/// \returns the size of the new attribute. -unsigned DwarfLinker::cloneScalarAttribute( +unsigned DwarfLinker::DIECloner::cloneScalarAttribute( DIE &Die, const DWARFDebugInfoEntryMinimal &InputDIE, CompileUnit &Unit, AttributeSpec AttrSpec, const DWARFFormValue &Val, unsigned AttrSize, AttributesInfo &Info) { @@ -1993,8 +2472,9 @@ unsigned DwarfLinker::cloneScalarAttribute( else if (auto OptionalValue = Val.getAsUnsignedConstant()) Value = *OptionalValue; else { - reportWarning("Unsupported scalar attribute form. Dropping attribute.", - &Unit.getOrigUnit(), &InputDIE); + Linker.reportWarning( + "Unsupported scalar attribute form. Dropping attribute.", + &Unit.getOrigUnit(), &InputDIE); return 0; } PatchLocation Patch = @@ -2002,6 +2482,7 @@ unsigned DwarfLinker::cloneScalarAttribute( dwarf::Form(AttrSpec.Form), DIEInteger(Value)); if (AttrSpec.Attr == dwarf::DW_AT_ranges) Unit.noteRangeAttribute(Die, Patch); + // A more generic way to check for location attributes would be // nice, but it's very unlikely that any other attribute needs a // location list. @@ -2017,12 +2498,10 @@ unsigned DwarfLinker::cloneScalarAttribute( /// \brief Clone \p InputDIE's attribute described by \p AttrSpec with /// value \p Val, and add it to \p Die. /// \returns the size of the cloned attribute. -unsigned DwarfLinker::cloneAttribute(DIE &Die, - const DWARFDebugInfoEntryMinimal &InputDIE, - CompileUnit &Unit, - const DWARFFormValue &Val, - const AttributeSpec AttrSpec, - unsigned AttrSize, AttributesInfo &Info) { +unsigned DwarfLinker::DIECloner::cloneAttribute( + DIE &Die, const DWARFDebugInfoEntryMinimal &InputDIE, CompileUnit &Unit, + const DWARFFormValue &Val, const AttributeSpec AttrSpec, unsigned AttrSize, + AttributesInfo &Info) { const DWARFUnit &U = Unit.getOrigUnit(); switch (AttrSpec.Form) { @@ -2056,8 +2535,9 @@ unsigned DwarfLinker::cloneAttribute(DIE &Die, return cloneScalarAttribute(Die, InputDIE, Unit, AttrSpec, Val, AttrSize, Info); default: - reportWarning("Unsupported attribute form in cloneAttribute. Dropping.", &U, - &InputDIE); + Linker.reportWarning( + "Unsupported attribute form in cloneAttribute. Dropping.", &U, + &InputDIE); } return 0; @@ -2071,8 +2551,9 @@ unsigned DwarfLinker::cloneAttribute(DIE &Die, /// monotonic \p BaseOffset values. /// /// \returns wether any reloc has been applied. -bool DwarfLinker::applyValidRelocs(MutableArrayRef<char> Data, - uint32_t BaseOffset, bool isLittleEndian) { +bool DwarfLinker::RelocationManager:: +applyValidRelocs(MutableArrayRef<char> Data, uint32_t BaseOffset, + bool isLittleEndian) { assert((NextValidReloc == 0 || BaseOffset > ValidRelocs[NextValidReloc - 1].Offset) && "BaseOffset should only be increasing."); @@ -2140,16 +2621,33 @@ static bool isTypeTag(uint16_t Tag) { return false; } -/// \brief Recursively clone \p InputDIE's subtrees that have been -/// selected to appear in the linked output. -/// -/// \param OutOffset is the Offset where the newly created DIE will -/// lie in the linked compile unit. -/// -/// \returns the cloned DIE object or null if nothing was selected. -DIE *DwarfLinker::cloneDIE(const DWARFDebugInfoEntryMinimal &InputDIE, - CompileUnit &Unit, int64_t PCOffset, - uint32_t OutOffset) { +static bool +shouldSkipAttribute(DWARFAbbreviationDeclaration::AttributeSpec AttrSpec, + uint16_t Tag, bool InDebugMap, bool SkipPC, + bool InFunctionScope) { + switch (AttrSpec.Attr) { + default: + return false; + case dwarf::DW_AT_low_pc: + case dwarf::DW_AT_high_pc: + case dwarf::DW_AT_ranges: + return SkipPC; + case dwarf::DW_AT_location: + case dwarf::DW_AT_frame_base: + // FIXME: for some reason dsymutil-classic keeps the location + // attributes when they are of block type (ie. not location + // lists). This is totally wrong for globals where we will keep a + // wrong address. It is mostly harmless for locals, but there is + // no point in keeping these anyway when the function wasn't linked. + return (SkipPC || (!InFunctionScope && Tag == dwarf::DW_TAG_variable && + !InDebugMap)) && + !DWARFFormValue(AttrSpec.Form).isFormClass(DWARFFormValue::FC_Block); + } +} + +DIE *DwarfLinker::DIECloner::cloneDIE( + const DWARFDebugInfoEntryMinimal &InputDIE, CompileUnit &Unit, + int64_t PCOffset, uint32_t OutOffset, unsigned Flags) { DWARFUnit &U = Unit.getOrigUnit(); unsigned Idx = U.getDIEIndex(&InputDIE); CompileUnit::DIEInfo &Info = Unit.getInfo(Idx); @@ -2166,10 +2664,25 @@ DIE *DwarfLinker::cloneDIE(const DWARFDebugInfoEntryMinimal &InputDIE, Die = Info.Clone = DIE::get(DIEAlloc, dwarf::Tag(InputDIE.getTag())); assert(Die->getTag() == InputDIE.getTag()); Die->setOffset(OutOffset); + if ((Unit.hasODR() || Unit.isClangModule()) && + Die->getTag() != dwarf::DW_TAG_namespace && Info.Ctxt && + Info.Ctxt != Unit.getInfo(Info.ParentIdx).Ctxt && + !Info.Ctxt->getCanonicalDIEOffset()) { + // We are about to emit a DIE that is the root of its own valid + // DeclContext tree. Make the current offset the canonical offset + // for this context. + Info.Ctxt->setCanonicalDIEOffset(OutOffset + Unit.getStartOffset()); + } // Extract and clone every attribute. DataExtractor Data = U.getDebugInfoExtractor(); - uint32_t NextOffset = U.getDIEAtIndex(Idx + 1)->getOffset(); + // Point to the next DIE (generally there is always at least a NULL + // entry after the current one). If this is a lone + // DW_TAG_compile_unit without any children, point to the next unit. + uint32_t NextOffset = + (Idx + 1 < U.getNumDIEs()) + ? U.getDIEAtIndex(Idx + 1)->getOffset() + : U.getNextUnitOffset(); AttributesInfo AttrInfo; // We could copy the data only if we need to aply a relocation to @@ -2178,7 +2691,7 @@ DIE *DwarfLinker::cloneDIE(const DWARFDebugInfoEntryMinimal &InputDIE, SmallString<40> DIECopy(Data.getData().substr(Offset, NextOffset - Offset)); Data = DataExtractor(DIECopy, Data.isLittleEndian(), Data.getAddressSize()); // Modify the copy with relocated addresses. - if (applyValidRelocs(DIECopy, Offset, Data.isLittleEndian())) { + if (RelocMgr.applyValidRelocs(DIECopy, Offset, Data.isLittleEndian())) { // If we applied relocations, we store the value of high_pc that was // potentially stored in the input DIE. If high_pc is an address // (Dwarf version == 2), then it might have been relocated to a @@ -2188,6 +2701,11 @@ DIE *DwarfLinker::cloneDIE(const DWARFDebugInfoEntryMinimal &InputDIE, // high_pc value is done in cloneAddressAttribute(). AttrInfo.OrigHighPc = InputDIE.getAttributeValueAsAddress(&U, dwarf::DW_AT_high_pc, 0); + // Also store the low_pc. It might get relocated in an + // inline_subprogram that happens at the beginning of its + // inlining function. + AttrInfo.OrigLowPc = + InputDIE.getAttributeValueAsAddress(&U, dwarf::DW_AT_low_pc, UINT64_MAX); } // Reset the Offset to 0 as we will be working on the local copy of @@ -2202,7 +2720,27 @@ DIE *DwarfLinker::cloneDIE(const DWARFDebugInfoEntryMinimal &InputDIE, PCOffset = Info.AddrAdjust; AttrInfo.PCOffset = PCOffset; + if (Abbrev->getTag() == dwarf::DW_TAG_subprogram) { + Flags |= TF_InFunctionScope; + if (!Info.InDebugMap) + Flags |= TF_SkipPC; + } + + bool Copied = false; for (const auto &AttrSpec : Abbrev->attributes()) { + if (shouldSkipAttribute(AttrSpec, Die->getTag(), Info.InDebugMap, + Flags & TF_SkipPC, Flags & TF_InFunctionScope)) { + DWARFFormValue::skipValue(AttrSpec.Form, Data, &Offset, &U); + // FIXME: dsymutil-classic keeps the old abbreviation around + // even if it's not used. We can remove this (and the copyAbbrev + // helper) as soon as bit-for-bit compatibility is not a goal anymore. + if (!Copied) { + copyAbbrev(*InputDIE.getAbbreviationDeclarationPtr(), Unit.hasODR()); + Copied = true; + } + continue; + } + DWARFFormValue Val(AttrSpec.Form); uint32_t AttrSize = Offset; Val.extractValue(Data, &Offset, &U); @@ -2232,20 +2770,28 @@ DIE *DwarfLinker::cloneDIE(const DWARFDebugInfoEntryMinimal &InputDIE, Unit.addTypeAccelerator(Die, AttrInfo.Name, AttrInfo.NameOffset); } + // Determine whether there are any children that we want to keep. + bool HasChildren = false; + for (auto *Child = InputDIE.getFirstChild(); Child && !Child->isNULL(); + Child = Child->getSibling()) { + unsigned Idx = U.getDIEIndex(Child); + if (Unit.getInfo(Idx).Keep) { + HasChildren = true; + break; + } + } + DIEAbbrev NewAbbrev = Die->generateAbbrev(); - // If a scope DIE is kept, we must have kept at least one child. If - // it's not the case, we'll just be emitting one wasteful end of - // children marker, but things won't break. - if (InputDIE.hasChildren()) + if (HasChildren) NewAbbrev.setChildrenFlag(dwarf::DW_CHILDREN_yes); // Assign a permanent abbrev number - AssignAbbrev(NewAbbrev); + Linker.AssignAbbrev(NewAbbrev); Die->setAbbrevNumber(NewAbbrev.getNumber()); // Add the size of the abbreviation number to the output offset. OutOffset += getULEB128Size(Die->getAbbrevNumber()); - if (!Abbrev->hasChildren()) { + if (!HasChildren) { // Update our size. Die->setSize(OutOffset - Die->getOffset()); return Die; @@ -2254,7 +2800,7 @@ DIE *DwarfLinker::cloneDIE(const DWARFDebugInfoEntryMinimal &InputDIE, // Recursively clone children. for (auto *Child = InputDIE.getFirstChild(); Child && !Child->isNULL(); Child = Child->getSibling()) { - if (DIE *Clone = cloneDIE(*Child, Unit, PCOffset, OutOffset)) { + if (DIE *Clone = cloneDIE(*Child, Unit, PCOffset, OutOffset, Flags)) { Die->addChild(Clone); OutOffset = Clone->getOffset() + Clone->getSize(); } @@ -2283,7 +2829,7 @@ void DwarfLinker::patchRangesForUnit(const CompileUnit &Unit, uint64_t OrigLowPc = OrigUnitDie->getAttributeValueAsAddress( &OrigUnit, dwarf::DW_AT_low_pc, -1ULL); // Ranges addresses are based on the unit's low_pc. Compute the - // offset we need to apply to adapt to the the new unit's low_pc. + // offset we need to apply to adapt to the new unit's low_pc. int64_t UnitPcOffset = 0; if (OrigLowPc != -1ULL) UnitPcOffset = int64_t(OrigLowPc) - Unit.getLowPc(); @@ -2293,15 +2839,18 @@ void DwarfLinker::patchRangesForUnit(const CompileUnit &Unit, RangeAttribute.set(Streamer->getRangesSectionSize()); RangeList.extract(RangeExtractor, &Offset); const auto &Entries = RangeList.getEntries(); - const DWARFDebugRangeList::RangeListEntry &First = Entries.front(); + if (!Entries.empty()) { + const DWARFDebugRangeList::RangeListEntry &First = Entries.front(); - if (CurrRange == InvalidRange || First.StartAddress < CurrRange.start() || - First.StartAddress >= CurrRange.stop()) { - CurrRange = FunctionRanges.find(First.StartAddress + OrigLowPc); if (CurrRange == InvalidRange || - CurrRange.start() > First.StartAddress + OrigLowPc) { - reportWarning("no mapping for range."); - continue; + First.StartAddress + OrigLowPc < CurrRange.start() || + First.StartAddress + OrigLowPc >= CurrRange.stop()) { + CurrRange = FunctionRanges.find(First.StartAddress + OrigLowPc); + if (CurrRange == InvalidRange || + CurrRange.start() > First.StartAddress + OrigLowPc) { + reportWarning("no mapping for range."); + continue; + } } } @@ -2451,12 +3000,13 @@ void DwarfLinker::patchLineTableForUnit(CompileUnit &Unit, if (StopAddress != -1ULL && !Seq.empty()) { // Insert end sequence row with the computed end address, but // the same line as the previous one. - Seq.emplace_back(Seq.back()); - Seq.back().Address = StopAddress; - Seq.back().EndSequence = 1; - Seq.back().PrologueEnd = 0; - Seq.back().BasicBlock = 0; - Seq.back().EpilogueBegin = 0; + auto NextLine = Seq.back(); + NextLine.Address = StopAddress; + NextLine.EndSequence = 1; + NextLine.PrologueEnd = 0; + NextLine.BasicBlock = 0; + NextLine.EpilogueBegin = 0; + Seq.push_back(NextLine); insertLineSequence(Seq, NewRows); } @@ -2484,13 +3034,18 @@ void DwarfLinker::patchLineTableForUnit(CompileUnit &Unit, // table emitter. if (LineTable.Prologue.Version != 2 || LineTable.Prologue.DefaultIsStmt != DWARF2_LINE_DEFAULT_IS_STMT || - LineTable.Prologue.LineBase != -5 || LineTable.Prologue.LineRange != 14 || - LineTable.Prologue.OpcodeBase != 13) + LineTable.Prologue.OpcodeBase > 13) reportWarning("line table paramters mismatch. Cannot emit."); - else - Streamer->emitLineTableForUnit(LineData.slice(StmtList + 4, PrologueEnd), + else { + MCDwarfLineTableParams Params; + Params.DWARF2LineOpcodeBase = LineTable.Prologue.OpcodeBase; + Params.DWARF2LineBase = LineTable.Prologue.LineBase; + Params.DWARF2LineRange = LineTable.Prologue.LineRange; + Streamer->emitLineTableForUnit(Params, + LineData.slice(StmtList + 4, PrologueEnd), LineTable.Prologue.MinInstLength, NewRows, Unit.getOrigUnit().getAddressByteSize()); + } } void DwarfLinker::emitAcceleratorEntriesForUnit(CompileUnit &Unit) { @@ -2583,33 +3138,211 @@ void DwarfLinker::patchFrameInfoForObject(const DebugMapObject &DMO, } } -bool DwarfLinker::link(const DebugMap &Map) { +void DwarfLinker::DIECloner::copyAbbrev( + const DWARFAbbreviationDeclaration &Abbrev, bool hasODR) { + DIEAbbrev Copy(dwarf::Tag(Abbrev.getTag()), + dwarf::Form(Abbrev.hasChildren())); + + for (const auto &Attr : Abbrev.attributes()) { + uint16_t Form = Attr.Form; + if (hasODR && isODRAttribute(Attr.Attr)) + Form = dwarf::DW_FORM_ref_addr; + Copy.AddAttribute(dwarf::Attribute(Attr.Attr), dwarf::Form(Form)); + } - if (Map.begin() == Map.end()) { - errs() << "Empty debug map.\n"; + Linker.AssignAbbrev(Copy); +} + +static uint64_t getDwoId(const DWARFDebugInfoEntryMinimal &CUDie, + const DWARFUnit &Unit) { + uint64_t DwoId = + CUDie.getAttributeValueAsUnsignedConstant(&Unit, dwarf::DW_AT_dwo_id, 0); + if (!DwoId) + DwoId = CUDie.getAttributeValueAsUnsignedConstant(&Unit, + dwarf::DW_AT_GNU_dwo_id, 0); + return DwoId; +} + +bool DwarfLinker::registerModuleReference( + const DWARFDebugInfoEntryMinimal &CUDie, const DWARFUnit &Unit, + DebugMap &ModuleMap, unsigned Indent) { + std::string PCMfile = + CUDie.getAttributeValueAsString(&Unit, dwarf::DW_AT_dwo_name, ""); + if (PCMfile.empty()) + PCMfile = + CUDie.getAttributeValueAsString(&Unit, dwarf::DW_AT_GNU_dwo_name, ""); + if (PCMfile.empty()) return false; + + // Clang module DWARF skeleton CUs abuse this for the path to the module. + std::string PCMpath = + CUDie.getAttributeValueAsString(&Unit, dwarf::DW_AT_comp_dir, ""); + uint64_t DwoId = getDwoId(CUDie, Unit); + + std::string Name = + CUDie.getAttributeValueAsString(&Unit, dwarf::DW_AT_name, ""); + if (Name.empty()) { + reportWarning("Anonymous module skeleton CU for " + PCMfile); + return true; + } + + if (Options.Verbose) { + outs().indent(Indent); + outs() << "Found clang module reference " << PCMfile; + } + + auto Cached = ClangModules.find(PCMfile); + if (Cached != ClangModules.end()) { + if (Cached->second != DwoId) + reportWarning(Twine("hash mismatch: this object file was built against a " + "different version of the module ") + PCMfile); + if (Options.Verbose) + outs() << " [cached].\n"; + return true; + } + if (Options.Verbose) + outs() << " ...\n"; + + // Cyclic dependencies are disallowed by Clang, but we still + // shouldn't run into an infinite loop, so mark it as processed now. + ClangModules.insert({PCMfile, DwoId}); + loadClangModule(PCMfile, PCMpath, Name, DwoId, ModuleMap, Indent + 2); + return true; +} + +ErrorOr<const object::ObjectFile &> +DwarfLinker::loadObject(BinaryHolder &BinaryHolder, DebugMapObject &Obj, + const DebugMap &Map) { + auto ErrOrObjs = + BinaryHolder.GetObjectFiles(Obj.getObjectFilename(), Obj.getTimestamp()); + if (std::error_code EC = ErrOrObjs.getError()) { + reportWarning(Twine(Obj.getObjectFilename()) + ": " + EC.message()); + return EC; + } + auto ErrOrObj = BinaryHolder.Get(Map.getTriple()); + if (std::error_code EC = ErrOrObj.getError()) + reportWarning(Twine(Obj.getObjectFilename()) + ": " + EC.message()); + return ErrOrObj; +} + +void DwarfLinker::loadClangModule(StringRef Filename, StringRef ModulePath, + StringRef ModuleName, uint64_t DwoId, + DebugMap &ModuleMap, unsigned Indent) { + SmallString<80> Path(Options.PrependPath); + if (sys::path::is_relative(Filename)) + sys::path::append(Path, ModulePath, Filename); + else + sys::path::append(Path, Filename); + BinaryHolder ObjHolder(Options.Verbose); + auto &Obj = + ModuleMap.addDebugMapObject(Path, sys::TimeValue::PosixZeroTime()); + auto ErrOrObj = loadObject(ObjHolder, Obj, ModuleMap); + if (!ErrOrObj) { + ClangModules.erase(ClangModules.find(Filename)); + return; + } + + std::unique_ptr<CompileUnit> Unit; + + // Setup access to the debug info. + DWARFContextInMemory DwarfContext(*ErrOrObj); + RelocationManager RelocMgr(*this); + for (const auto &CU : DwarfContext.compile_units()) { + auto *CUDie = CU->getUnitDIE(false); + // Recursively get all modules imported by this one. + if (!registerModuleReference(*CUDie, *CU, ModuleMap, Indent)) { + if (Unit) { + errs() << Filename << ": Clang modules are expected to have exactly" + << " 1 compile unit.\n"; + exitDsymutil(1); + } + if (getDwoId(*CUDie, *CU) != DwoId) + reportWarning( + Twine("hash mismatch: this object file was built against a " + "different version of the module ") + Filename); + + // Add this module. + Unit = llvm::make_unique<CompileUnit>(*CU, UnitID++, !Options.NoODR, + ModuleName); + Unit->setHasInterestingContent(); + analyzeContextInfo(CUDie, 0, *Unit, &ODRContexts.getRoot(), StringPool, + ODRContexts); + // Keep everything. + Unit->markEverythingAsKept(); + } + } + if (Options.Verbose) { + outs().indent(Indent); + outs() << "cloning .debug_info from " << Filename << "\n"; + } + + DIECloner(*this, RelocMgr, DIEAlloc, MutableArrayRef<CompileUnit>(*Unit), + Options) + .cloneAllCompileUnits(DwarfContext); +} + +void DwarfLinker::DIECloner::cloneAllCompileUnits( + DWARFContextInMemory &DwarfContext) { + if (!Linker.Streamer) + return; + + for (auto &CurrentUnit : CompileUnits) { + const auto *InputDIE = CurrentUnit.getOrigUnit().getUnitDIE(); + CurrentUnit.setStartOffset(Linker.OutputDebugInfoSize); + DIE *OutputDIE = cloneDIE(*InputDIE, CurrentUnit, 0 /* PC offset */, + 11 /* Unit Header size */, 0); + CurrentUnit.setOutputUnitDIE(OutputDIE); + Linker.OutputDebugInfoSize = CurrentUnit.computeNextUnitOffset(); + if (Linker.Options.NoOutput) + continue; + // FIXME: for compatibility with the classic dsymutil, we emit + // an empty line table for the unit, even if the unit doesn't + // actually exist in the DIE tree. + Linker.patchLineTableForUnit(CurrentUnit, DwarfContext); + if (!OutputDIE) + continue; + Linker.patchRangesForUnit(CurrentUnit, DwarfContext); + Linker.Streamer->emitLocationsForUnit(CurrentUnit, DwarfContext); + Linker.emitAcceleratorEntriesForUnit(CurrentUnit); } + if (Linker.Options.NoOutput) + return; + + // Emit all the compile unit's debug information. + for (auto &CurrentUnit : CompileUnits) { + Linker.generateUnitRanges(CurrentUnit); + CurrentUnit.fixupForwardReferences(); + Linker.Streamer->emitCompileUnitHeader(CurrentUnit); + if (!CurrentUnit.getOutputUnitDIE()) + continue; + Linker.Streamer->emitDIE(*CurrentUnit.getOutputUnitDIE()); + } +} + +bool DwarfLinker::link(const DebugMap &Map) { + if (!createStreamer(Map.getTriple(), OutputFilename)) return false; // Size of the DIEs (and headers) generated for the linked output. - uint64_t OutputDebugInfoSize = 0; + OutputDebugInfoSize = 0; // A unique ID that identifies each compile unit. - unsigned UnitID = 0; + UnitID = 0; + DebugMap ModuleMap(Map.getTriple(), Map.getBinaryPath()); + for (const auto &Obj : Map.objects()) { CurrentDebugObject = Obj.get(); if (Options.Verbose) outs() << "DEBUG MAP OBJECT: " << Obj->getObjectFilename() << "\n"; - auto ErrOrObj = BinHolder.GetObjectFile(Obj->getObjectFilename()); - if (std::error_code EC = ErrOrObj.getError()) { - reportWarning(Twine(Obj->getObjectFilename()) + ": " + EC.message()); + auto ErrOrObj = loadObject(BinHolder, *Obj, Map); + if (!ErrOrObj) continue; - } // Look for relocations that correspond to debug map entries. - if (!findValidRelocsInDebugInfo(*ErrOrObj, *Obj)) { + RelocationManager RelocMgr(*this); + if (!RelocMgr.findValidRelocsInDebugInfo(*ErrOrObj, *Obj)) { if (Options.Verbose) outs() << "No valid relocations found. Skipping.\n"; continue; @@ -2619,68 +3352,40 @@ bool DwarfLinker::link(const DebugMap &Map) { DWARFContextInMemory DwarfContext(*ErrOrObj); startDebugObject(DwarfContext, *Obj); - // In a first phase, just read in the debug info and store the DIE - // parent links that we will use during the next phase. + // In a first phase, just read in the debug info and load all clang modules. for (const auto &CU : DwarfContext.compile_units()) { auto *CUDie = CU->getUnitDIE(false); if (Options.Verbose) { outs() << "Input compilation unit:"; CUDie->dump(outs(), CU.get(), 0); } - Units.emplace_back(*CU, UnitID++); - gatherDIEParents(CUDie, 0, Units.back()); + + if (!registerModuleReference(*CUDie, *CU, ModuleMap)) + Units.emplace_back(*CU, UnitID++, !Options.NoODR, ""); } + // Now build the DIE parent links that we will use during the next phase. + for (auto &CurrentUnit : Units) + analyzeContextInfo(CurrentUnit.getOrigUnit().getUnitDIE(), 0, CurrentUnit, + &ODRContexts.getRoot(), StringPool, ODRContexts); + // Then mark all the DIEs that need to be present in the linked // output and collect some information about them. Note that this // loop can not be merged with the previous one becaue cross-cu // references require the ParentIdx to be setup for every CU in // the object file before calling this. for (auto &CurrentUnit : Units) - lookForDIEsToKeep(*CurrentUnit.getOrigUnit().getUnitDIE(), *Obj, + lookForDIEsToKeep(RelocMgr, *CurrentUnit.getOrigUnit().getUnitDIE(), *Obj, CurrentUnit, 0); // The calls to applyValidRelocs inside cloneDIE will walk the // reloc array again (in the same way findValidRelocsInDebugInfo() // did). We need to reset the NextValidReloc index to the beginning. - NextValidReloc = 0; - - // Construct the output DIE tree by cloning the DIEs we chose to - // keep above. If there are no valid relocs, then there's nothing - // to clone/emit. - if (!ValidRelocs.empty()) - for (auto &CurrentUnit : Units) { - const auto *InputDIE = CurrentUnit.getOrigUnit().getUnitDIE(); - CurrentUnit.setStartOffset(OutputDebugInfoSize); - DIE *OutputDIE = cloneDIE(*InputDIE, CurrentUnit, 0 /* PCOffset */, - 11 /* Unit Header size */); - CurrentUnit.setOutputUnitDIE(OutputDIE); - OutputDebugInfoSize = CurrentUnit.computeNextUnitOffset(); - if (Options.NoOutput) - continue; - // FIXME: for compatibility with the classic dsymutil, we emit - // an empty line table for the unit, even if the unit doesn't - // actually exist in the DIE tree. - patchLineTableForUnit(CurrentUnit, DwarfContext); - if (!OutputDIE) - continue; - patchRangesForUnit(CurrentUnit, DwarfContext); - Streamer->emitLocationsForUnit(CurrentUnit, DwarfContext); - emitAcceleratorEntriesForUnit(CurrentUnit); - } - - // Emit all the compile unit's debug information. - if (!ValidRelocs.empty() && !Options.NoOutput) - for (auto &CurrentUnit : Units) { - generateUnitRanges(CurrentUnit); - CurrentUnit.fixupForwardReferences(); - Streamer->emitCompileUnitHeader(CurrentUnit); - if (!CurrentUnit.getOutputUnitDIE()) - continue; - Streamer->emitDIE(*CurrentUnit.getOutputUnitDIE()); - } - - if (!ValidRelocs.empty() && !Options.NoOutput && !Units.empty()) + RelocMgr.resetValidRelocs(); + if (RelocMgr.hasValidRelocs()) + DIECloner(*this, RelocMgr, DIEAlloc, Units, Options) + .cloneAllCompileUnits(DwarfContext); + if (!Options.NoOutput && !Units.empty()) patchFrameInfoForObject(*Obj, DwarfContext, Units[0].getOrigUnit().getAddressByteSize()); @@ -2694,8 +3399,53 @@ bool DwarfLinker::link(const DebugMap &Map) { Streamer->emitStrings(StringPool); } - return Options.NoOutput ? true : Streamer->finish(); + return Options.NoOutput ? true : Streamer->finish(Map); +} } + +/// \brief Get the offset of string \p S in the string table. This +/// can insert a new element or return the offset of a preexisitng +/// one. +uint32_t NonRelocatableStringpool::getStringOffset(StringRef S) { + if (S.empty() && !Strings.empty()) + return 0; + + std::pair<uint32_t, StringMapEntryBase *> Entry(0, nullptr); + MapTy::iterator It; + bool Inserted; + + // A non-empty string can't be at offset 0, so if we have an entry + // with a 0 offset, it must be a previously interned string. + std::tie(It, Inserted) = Strings.insert(std::make_pair(S, Entry)); + if (Inserted || It->getValue().first == 0) { + // Set offset and chain at the end of the entries list. + It->getValue().first = CurrentEndOffset; + CurrentEndOffset += S.size() + 1; // +1 for the '\0'. + Last->getValue().second = &*It; + Last = &*It; + } + return It->getValue().first; +} + +/// \brief Put \p S into the StringMap so that it gets permanent +/// storage, but do not actually link it in the chain of elements +/// that go into the output section. A latter call to +/// getStringOffset() with the same string will chain it though. +StringRef NonRelocatableStringpool::internString(StringRef S) { + std::pair<uint32_t, StringMapEntryBase *> Entry(0, nullptr); + auto InsertResult = Strings.insert(std::make_pair(S, Entry)); + return InsertResult.first->getKey(); +} + +void warn(const Twine &Warning, const Twine &Context) { + errs() << Twine("while processing ") + Context + ":\n"; + errs() << Twine("warning: ") + Warning + "\n"; +} + +bool error(const Twine &Error, const Twine &Context) { + errs() << Twine("while processing ") + Context + ":\n"; + errs() << Twine("error: ") + Error + "\n"; + return false; } bool linkDwarf(StringRef OutputFilename, const DebugMap &DM, diff --git a/tools/dsymutil/MachODebugMapParser.cpp b/tools/dsymutil/MachODebugMapParser.cpp index 6c9fa9b51325..4412db25426c 100644 --- a/tools/dsymutil/MachODebugMapParser.cpp +++ b/tools/dsymutil/MachODebugMapParser.cpp @@ -10,6 +10,7 @@ #include "BinaryHolder.h" #include "DebugMap.h" #include "dsymutil.h" +#include "llvm/ADT/Optional.h" #include "llvm/Object/MachO.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" @@ -21,19 +22,25 @@ using namespace llvm::object; class MachODebugMapParser { public: - MachODebugMapParser(StringRef BinaryPath, StringRef PathPrefix = "", - bool Verbose = false) - : BinaryPath(BinaryPath), PathPrefix(PathPrefix), - MainBinaryHolder(Verbose), CurrentObjectHolder(Verbose), - CurrentDebugMapObject(nullptr) {} + MachODebugMapParser(StringRef BinaryPath, ArrayRef<std::string> Archs, + StringRef PathPrefix = "", bool Verbose = false) + : BinaryPath(BinaryPath), Archs(Archs.begin(), Archs.end()), + PathPrefix(PathPrefix), MainBinaryHolder(Verbose), + CurrentObjectHolder(Verbose), CurrentDebugMapObject(nullptr) {} - /// \brief Parses and returns the DebugMap of the input binary. + /// \brief Parses and returns the DebugMaps of the input binary. + /// The binary contains multiple maps in case it is a universal + /// binary. /// \returns an error in case the provided BinaryPath doesn't exist /// or isn't of a supported type. - ErrorOr<std::unique_ptr<DebugMap>> parse(); + ErrorOr<std::vector<std::unique_ptr<DebugMap>>> parse(); + + /// Walk the symbol table and dump it. + bool dumpStab(); private: std::string BinaryPath; + SmallVector<StringRef, 1> Archs; std::string PathPrefix; /// Owns the MemoryBuffer for the main binary. @@ -47,7 +54,7 @@ private: /// Owns the MemoryBuffer for the currently handled object file. BinaryHolder CurrentObjectHolder; /// Map of the currently processed object file symbol addresses. - StringMap<uint64_t> CurrentObjectAddresses; + StringMap<Optional<uint64_t>> CurrentObjectAddresses; /// Element of the debug map corresponfing to the current object file. DebugMapObject *CurrentDebugMapObject; @@ -55,11 +62,14 @@ private: const char *CurrentFunctionName; uint64_t CurrentFunctionAddress; - void switchToNewDebugMapObject(StringRef Filename); + std::unique_ptr<DebugMap> parseOneBinary(const MachOObjectFile &MainBinary, + StringRef BinaryPath); + + void switchToNewDebugMapObject(StringRef Filename, sys::TimeValue Timestamp); void resetParserState(); uint64_t getMainBinarySymbolAddress(StringRef Name); - void loadMainBinarySymbols(); - void loadCurrentObjectFileSymbols(); + void loadMainBinarySymbols(const MachOObjectFile &MainBinary); + void loadCurrentObjectFileSymbols(const object::MachOObjectFile &Obj); void handleStabSymbolTableEntry(uint32_t StringIndex, uint8_t Type, uint8_t SectionIndex, uint16_t Flags, uint64_t Value); @@ -68,10 +78,26 @@ private: handleStabSymbolTableEntry(STE.n_strx, STE.n_type, STE.n_sect, STE.n_desc, STE.n_value); } + + /// Dump the symbol table output header. + void dumpSymTabHeader(raw_ostream &OS, StringRef Arch); + + /// Dump the contents of nlist entries. + void dumpSymTabEntry(raw_ostream &OS, uint64_t Index, uint32_t StringIndex, + uint8_t Type, uint8_t SectionIndex, uint16_t Flags, + uint64_t Value); + + template <typename STEType> + void dumpSymTabEntry(raw_ostream &OS, uint64_t Index, const STEType &STE) { + dumpSymTabEntry(OS, Index, STE.n_strx, STE.n_type, STE.n_sect, STE.n_desc, + STE.n_value); + } + void dumpOneBinaryStab(const MachOObjectFile &MainBinary, + StringRef BinaryPath); }; static void Warning(const Twine &Msg) { errs() << "warning: " + Msg + "\n"; } -} +} // anonymous namespace /// Reset the parser state coresponding to the current object /// file. This is to be called after an object file is finished @@ -84,41 +110,44 @@ void MachODebugMapParser::resetParserState() { /// Create a new DebugMapObject. This function resets the state of the /// parser that was referring to the last object file and sets /// everything up to add symbols to the new one. -void MachODebugMapParser::switchToNewDebugMapObject(StringRef Filename) { +void MachODebugMapParser::switchToNewDebugMapObject(StringRef Filename, + sys::TimeValue Timestamp) { resetParserState(); SmallString<80> Path(PathPrefix); sys::path::append(Path, Filename); - auto MachOOrError = CurrentObjectHolder.GetFileAs<MachOObjectFile>(Path); + auto MachOOrError = + CurrentObjectHolder.GetFilesAs<MachOObjectFile>(Path, Timestamp); if (auto Error = MachOOrError.getError()) { Warning(Twine("cannot open debug object \"") + Path.str() + "\": " + Error.message() + "\n"); return; } - loadCurrentObjectFileSymbols(); - CurrentDebugMapObject = &Result->addDebugMapObject(Path); -} + auto ErrOrAchObj = + CurrentObjectHolder.GetAs<MachOObjectFile>(Result->getTriple()); + if (auto Err = ErrOrAchObj.getError()) { + return Warning(Twine("cannot open debug object \"") + Path.str() + "\": " + + Err.message() + "\n"); + } -static Triple getTriple(const object::MachOObjectFile &Obj) { - Triple TheTriple("unknown-unknown-unknown"); - TheTriple.setArch(Triple::ArchType(Obj.getArch())); - TheTriple.setObjectFormat(Triple::MachO); - return TheTriple; + CurrentDebugMapObject = &Result->addDebugMapObject(Path, Timestamp); + loadCurrentObjectFileSymbols(*ErrOrAchObj); } -/// This main parsing routine tries to open the main binary and if -/// successful iterates over the STAB entries. The real parsing is -/// done in handleStabSymbolTableEntry. -ErrorOr<std::unique_ptr<DebugMap>> MachODebugMapParser::parse() { - auto MainBinOrError = MainBinaryHolder.GetFileAs<MachOObjectFile>(BinaryPath); - if (auto Error = MainBinOrError.getError()) - return Error; +static std::string getArchName(const object::MachOObjectFile &Obj) { + Triple ThumbTriple; + Triple T = Obj.getArch(nullptr, &ThumbTriple); + return T.getArchName(); +} - const MachOObjectFile &MainBinary = *MainBinOrError; - loadMainBinarySymbols(); - Result = make_unique<DebugMap>(getTriple(MainBinary)); +std::unique_ptr<DebugMap> +MachODebugMapParser::parseOneBinary(const MachOObjectFile &MainBinary, + StringRef BinaryPath) { + loadMainBinarySymbols(MainBinary); + Result = + make_unique<DebugMap>(BinaryHolder::getTriple(MainBinary), BinaryPath); MainBinaryStrings = MainBinary.getStringTableData(); for (const SymbolRef &Symbol : MainBinary.symbols()) { const DataRefImpl &DRI = Symbol.getRawDataRefImpl(); @@ -132,6 +161,179 @@ ErrorOr<std::unique_ptr<DebugMap>> MachODebugMapParser::parse() { return std::move(Result); } +// Table that maps Darwin's Mach-O stab constants to strings to allow printing. +// llvm-nm has very similar code, the strings used here are however slightly +// different and part of the interface of dsymutil (some project's build-systems +// parse the ouptut of dsymutil -s), thus they shouldn't be changed. +struct DarwinStabName { + uint8_t NType; + const char *Name; +}; + +static const struct DarwinStabName DarwinStabNames[] = { + {MachO::N_GSYM, "N_GSYM"}, {MachO::N_FNAME, "N_FNAME"}, + {MachO::N_FUN, "N_FUN"}, {MachO::N_STSYM, "N_STSYM"}, + {MachO::N_LCSYM, "N_LCSYM"}, {MachO::N_BNSYM, "N_BNSYM"}, + {MachO::N_PC, "N_PC"}, {MachO::N_AST, "N_AST"}, + {MachO::N_OPT, "N_OPT"}, {MachO::N_RSYM, "N_RSYM"}, + {MachO::N_SLINE, "N_SLINE"}, {MachO::N_ENSYM, "N_ENSYM"}, + {MachO::N_SSYM, "N_SSYM"}, {MachO::N_SO, "N_SO"}, + {MachO::N_OSO, "N_OSO"}, {MachO::N_LSYM, "N_LSYM"}, + {MachO::N_BINCL, "N_BINCL"}, {MachO::N_SOL, "N_SOL"}, + {MachO::N_PARAMS, "N_PARAM"}, {MachO::N_VERSION, "N_VERS"}, + {MachO::N_OLEVEL, "N_OLEV"}, {MachO::N_PSYM, "N_PSYM"}, + {MachO::N_EINCL, "N_EINCL"}, {MachO::N_ENTRY, "N_ENTRY"}, + {MachO::N_LBRAC, "N_LBRAC"}, {MachO::N_EXCL, "N_EXCL"}, + {MachO::N_RBRAC, "N_RBRAC"}, {MachO::N_BCOMM, "N_BCOMM"}, + {MachO::N_ECOMM, "N_ECOMM"}, {MachO::N_ECOML, "N_ECOML"}, + {MachO::N_LENG, "N_LENG"}, {0, nullptr}}; + +static const char *getDarwinStabString(uint8_t NType) { + for (unsigned i = 0; DarwinStabNames[i].Name; i++) { + if (DarwinStabNames[i].NType == NType) + return DarwinStabNames[i].Name; + } + return nullptr; +} + +void MachODebugMapParser::dumpSymTabHeader(raw_ostream &OS, StringRef Arch) { + OS << "-----------------------------------" + "-----------------------------------\n"; + OS << "Symbol table for: '" << BinaryPath << "' (" << Arch.data() << ")\n"; + OS << "-----------------------------------" + "-----------------------------------\n"; + OS << "Index n_strx n_type n_sect n_desc n_value\n"; + OS << "======== -------- ------------------ ------ ------ ----------------\n"; +} + +void MachODebugMapParser::dumpSymTabEntry(raw_ostream &OS, uint64_t Index, + uint32_t StringIndex, uint8_t Type, + uint8_t SectionIndex, uint16_t Flags, + uint64_t Value) { + // Index + OS << '[' << format_decimal(Index, 6) << "] " + // n_strx + << format_hex_no_prefix(StringIndex, 8) << ' ' + // n_type... + << format_hex_no_prefix(Type, 2) << " ("; + + if (Type & MachO::N_STAB) + OS << left_justify(getDarwinStabString(Type), 13); + else { + if (Type & MachO::N_PEXT) + OS << "PEXT "; + else + OS << " "; + switch (Type & MachO::N_TYPE) { + case MachO::N_UNDF: // 0x0 undefined, n_sect == NO_SECT + OS << "UNDF"; + break; + case MachO::N_ABS: // 0x2 absolute, n_sect == NO_SECT + OS << "ABS "; + break; + case MachO::N_SECT: // 0xe defined in section number n_sect + OS << "SECT"; + break; + case MachO::N_PBUD: // 0xc prebound undefined (defined in a dylib) + OS << "PBUD"; + break; + case MachO::N_INDR: // 0xa indirect + OS << "INDR"; + break; + default: + OS << format_hex_no_prefix(Type, 2) << " "; + break; + } + if (Type & MachO::N_EXT) + OS << " EXT"; + else + OS << " "; + } + + OS << ") " + // n_sect + << format_hex_no_prefix(SectionIndex, 2) << " " + // n_desc + << format_hex_no_prefix(Flags, 4) << " " + // n_value + << format_hex_no_prefix(Value, 16); + + const char *Name = &MainBinaryStrings.data()[StringIndex]; + if (Name && Name[0]) + OS << " '" << Name << "'"; + + OS << "\n"; +} + +void MachODebugMapParser::dumpOneBinaryStab(const MachOObjectFile &MainBinary, + StringRef BinaryPath) { + loadMainBinarySymbols(MainBinary); + MainBinaryStrings = MainBinary.getStringTableData(); + raw_ostream &OS(llvm::outs()); + + dumpSymTabHeader(OS, getArchName(MainBinary)); + uint64_t Idx = 0; + for (const SymbolRef &Symbol : MainBinary.symbols()) { + const DataRefImpl &DRI = Symbol.getRawDataRefImpl(); + if (MainBinary.is64Bit()) + dumpSymTabEntry(OS, Idx, MainBinary.getSymbol64TableEntry(DRI)); + else + dumpSymTabEntry(OS, Idx, MainBinary.getSymbolTableEntry(DRI)); + Idx++; + } + + OS << "\n\n"; + resetParserState(); +} + +static bool shouldLinkArch(SmallVectorImpl<StringRef> &Archs, StringRef Arch) { + if (Archs.empty() || + std::find(Archs.begin(), Archs.end(), "all") != Archs.end() || + std::find(Archs.begin(), Archs.end(), "*") != Archs.end()) + return true; + + if (Arch.startswith("arm") && Arch != "arm64" && + std::find(Archs.begin(), Archs.end(), "arm") != Archs.end()) + return true; + + return std::find(Archs.begin(), Archs.end(), Arch) != Archs.end(); +} + +bool MachODebugMapParser::dumpStab() { + auto MainBinOrError = + MainBinaryHolder.GetFilesAs<MachOObjectFile>(BinaryPath); + if (auto Error = MainBinOrError.getError()) { + llvm::errs() << "Cannot get '" << BinaryPath + << "' as MachO file: " << Error.message() << "\n"; + return false; + } + + Triple T; + for (const auto *Binary : *MainBinOrError) + if (shouldLinkArch(Archs, Binary->getArch(nullptr, &T).getArchName())) + dumpOneBinaryStab(*Binary, BinaryPath); + + return true; +} + +/// This main parsing routine tries to open the main binary and if +/// successful iterates over the STAB entries. The real parsing is +/// done in handleStabSymbolTableEntry. +ErrorOr<std::vector<std::unique_ptr<DebugMap>>> MachODebugMapParser::parse() { + auto MainBinOrError = + MainBinaryHolder.GetFilesAs<MachOObjectFile>(BinaryPath); + if (auto Error = MainBinOrError.getError()) + return Error; + + std::vector<std::unique_ptr<DebugMap>> Results; + Triple T; + for (const auto *Binary : *MainBinOrError) + if (shouldLinkArch(Archs, Binary->getArch(nullptr, &T).getArchName())) + Results.push_back(parseOneBinary(*Binary, BinaryPath)); + + return std::move(Results); +} + /// Interpret the STAB entries to fill the DebugMap. void MachODebugMapParser::handleStabSymbolTableEntry(uint32_t StringIndex, uint8_t Type, @@ -144,8 +346,11 @@ void MachODebugMapParser::handleStabSymbolTableEntry(uint32_t StringIndex, const char *Name = &MainBinaryStrings.data()[StringIndex]; // An N_OSO entry represents the start of a new object file description. - if (Type == MachO::N_OSO) - return switchToNewDebugMapObject(Name); + if (Type == MachO::N_OSO) { + sys::TimeValue Timestamp; + Timestamp.fromEpochTime(Value); + return switchToNewDebugMapObject(Name, Timestamp); + } // If the last N_OSO object file wasn't found, // CurrentDebugMapObject will be null. Do not update anything @@ -184,22 +389,33 @@ void MachODebugMapParser::handleStabSymbolTableEntry(uint32_t StringIndex, if (ObjectSymIt == CurrentObjectAddresses.end()) return Warning("could not find object file symbol for symbol " + Twine(Name)); - if (!CurrentDebugMapObject->addSymbol(Name, ObjectSymIt->getValue(), Value, + if (!ObjectSymIt->getValue()) + return; + if (!CurrentDebugMapObject->addSymbol(Name, *ObjectSymIt->getValue(), Value, Size)) return Warning(Twine("failed to insert symbol '") + Name + "' in the debug map."); } /// Load the current object file symbols into CurrentObjectAddresses. -void MachODebugMapParser::loadCurrentObjectFileSymbols() { +void MachODebugMapParser::loadCurrentObjectFileSymbols( + const object::MachOObjectFile &Obj) { CurrentObjectAddresses.clear(); - for (auto Sym : CurrentObjectHolder.Get().symbols()) { + for (auto Sym : Obj.symbols()) { uint64_t Addr = Sym.getValue(); ErrorOr<StringRef> Name = Sym.getName(); if (!Name) continue; - CurrentObjectAddresses[*Name] = Addr; + // Objective-C on i386 uses artificial absolute symbols to + // perform some link time checks. Those symbols have a fixed 0 + // address that might conflict with real symbols in the object + // file. As I cannot see a way for absolute symbols to find + // their way into the debug information, let's just ignore those. + if (Sym.getFlags() & SymbolRef::SF_Absolute) + CurrentObjectAddresses[*Name] = None; + else + CurrentObjectAddresses[*Name] = Addr; } } @@ -215,9 +431,10 @@ uint64_t MachODebugMapParser::getMainBinarySymbolAddress(StringRef Name) { /// Load the interesting main binary symbols' addresses into /// MainBinarySymbolAddresses. -void MachODebugMapParser::loadMainBinarySymbols() { - const MachOObjectFile &MainBinary = MainBinaryHolder.GetAs<MachOObjectFile>(); +void MachODebugMapParser::loadMainBinarySymbols( + const MachOObjectFile &MainBinary) { section_iterator Section = MainBinary.section_end(); + MainBinarySymbolAddresses.clear(); for (const auto &Sym : MainBinary.symbols()) { SymbolRef::Type Type = Sym.getType(); // Skip undefined and STAB entries. @@ -227,8 +444,13 @@ void MachODebugMapParser::loadMainBinarySymbols() { // are the only ones that need to be queried because the address // of common data won't be described in the debug map. All other // addresses should be fetched for the debug map. - if (!(Sym.getFlags() & SymbolRef::SF_Global) || Sym.getSection(Section) || - Section == MainBinary.section_end() || Section->isText()) + if (!(Sym.getFlags() & SymbolRef::SF_Global)) + continue; + ErrorOr<section_iterator> SectionOrErr = Sym.getSection(); + if (!SectionOrErr) + continue; + Section = *SectionOrErr; + if (Section == MainBinary.section_end() || Section->isText()) continue; uint64_t Addr = Sym.getValue(); ErrorOr<StringRef> NameOrErr = Sym.getName(); @@ -243,16 +465,21 @@ void MachODebugMapParser::loadMainBinarySymbols() { namespace llvm { namespace dsymutil { -llvm::ErrorOr<std::unique_ptr<DebugMap>> parseDebugMap(StringRef InputFile, - StringRef PrependPath, - bool Verbose, - bool InputIsYAML) { +llvm::ErrorOr<std::vector<std::unique_ptr<DebugMap>>> +parseDebugMap(StringRef InputFile, ArrayRef<std::string> Archs, + StringRef PrependPath, bool Verbose, bool InputIsYAML) { if (!InputIsYAML) { - MachODebugMapParser Parser(InputFile, PrependPath, Verbose); + MachODebugMapParser Parser(InputFile, Archs, PrependPath, Verbose); return Parser.parse(); } else { return DebugMap::parseYAMLDebugMap(InputFile, PrependPath, Verbose); } } + +bool dumpStab(StringRef InputFile, ArrayRef<std::string> Archs, + StringRef PrependPath) { + MachODebugMapParser Parser(InputFile, Archs, PrependPath, false); + return Parser.dumpStab(); } -} +} // namespace dsymutil +} // namespace llvm diff --git a/tools/dsymutil/MachOUtils.cpp b/tools/dsymutil/MachOUtils.cpp new file mode 100644 index 000000000000..44cc528ed8f2 --- /dev/null +++ b/tools/dsymutil/MachOUtils.cpp @@ -0,0 +1,521 @@ +//===-- MachOUtils.h - Mach-o specific helpers for dsymutil --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MachOUtils.h" +#include "BinaryHolder.h" +#include "DebugMap.h" +#include "dsymutil.h" +#include "NonRelocatableStringpool.h" +#include "llvm/MC/MCSectionMachO.h" +#include "llvm/MC/MCAsmLayout.h" +#include "llvm/MC/MCSectionMachO.h" +#include "llvm/MC/MCObjectStreamer.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/Object/MachO.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace dsymutil { +namespace MachOUtils { + +std::string getArchName(StringRef Arch) { + if (Arch.startswith("thumb")) + return (llvm::Twine("arm") + Arch.drop_front(5)).str(); + return Arch; +} + +static bool runLipo(StringRef SDKPath, SmallVectorImpl<const char *> &Args) { + auto Path = sys::findProgramByName("lipo", makeArrayRef(SDKPath)); + if (!Path) + Path = sys::findProgramByName("lipo"); + + if (!Path) { + errs() << "error: lipo: " << Path.getError().message() << "\n"; + return false; + } + + std::string ErrMsg; + int result = + sys::ExecuteAndWait(*Path, Args.data(), nullptr, nullptr, 0, 0, &ErrMsg); + if (result) { + errs() << "error: lipo: " << ErrMsg << "\n"; + return false; + } + + return true; +} + +bool generateUniversalBinary(SmallVectorImpl<ArchAndFilename> &ArchFiles, + StringRef OutputFileName, + const LinkOptions &Options, StringRef SDKPath) { + // No need to merge one file into a universal fat binary. First, try + // to move it (rename) to the final location. If that fails because + // of cross-device link issues then copy and delete. + if (ArchFiles.size() == 1) { + StringRef From(ArchFiles.front().Path); + if (sys::fs::rename(From, OutputFileName)) { + if (std::error_code EC = sys::fs::copy_file(From, OutputFileName)) { + errs() << "error: while copying " << From << " to " << OutputFileName + << ": " << EC.message() << "\n"; + return false; + } + sys::fs::remove(From); + } + return true; + } + + SmallVector<const char *, 8> Args; + Args.push_back("lipo"); + Args.push_back("-create"); + + for (auto &Thin : ArchFiles) + Args.push_back(Thin.Path.c_str()); + + // Align segments to match dsymutil-classic alignment + for (auto &Thin : ArchFiles) { + Thin.Arch = getArchName(Thin.Arch); + Args.push_back("-segalign"); + Args.push_back(Thin.Arch.c_str()); + Args.push_back("20"); + } + + Args.push_back("-output"); + Args.push_back(OutputFileName.data()); + Args.push_back(nullptr); + + if (Options.Verbose) { + outs() << "Running lipo\n"; + for (auto Arg : Args) + outs() << ' ' << ((Arg == nullptr) ? "\n" : Arg); + } + + return Options.NoOutput ? true : runLipo(SDKPath, Args); +} + +// Return a MachO::segment_command_64 that holds the same values as +// the passed MachO::segment_command. We do that to avoid having to +// duplicat the logic for 32bits and 64bits segments. +struct MachO::segment_command_64 adaptFrom32bits(MachO::segment_command Seg) { + MachO::segment_command_64 Seg64; + Seg64.cmd = Seg.cmd; + Seg64.cmdsize = Seg.cmdsize; + memcpy(Seg64.segname, Seg.segname, sizeof(Seg.segname)); + Seg64.vmaddr = Seg.vmaddr; + Seg64.vmsize = Seg.vmsize; + Seg64.fileoff = Seg.fileoff; + Seg64.filesize = Seg.filesize; + Seg64.maxprot = Seg.maxprot; + Seg64.initprot = Seg.initprot; + Seg64.nsects = Seg.nsects; + Seg64.flags = Seg.flags; + return Seg64; +} + +// Iterate on all \a Obj segments, and apply \a Handler to them. +template <typename FunctionTy> +static void iterateOnSegments(const object::MachOObjectFile &Obj, + FunctionTy Handler) { + for (const auto &LCI : Obj.load_commands()) { + MachO::segment_command_64 Segment; + if (LCI.C.cmd == MachO::LC_SEGMENT) + Segment = adaptFrom32bits(Obj.getSegmentLoadCommand(LCI)); + else if (LCI.C.cmd == MachO::LC_SEGMENT_64) + Segment = Obj.getSegment64LoadCommand(LCI); + else + continue; + + Handler(Segment); + } +} + +// Transfer the symbols described by \a NList to \a NewSymtab which is +// just the raw contents of the symbol table for the dSYM companion file. +// \returns whether the symbol was tranfered or not. +template <typename NListTy> +static bool transferSymbol(NListTy NList, bool IsLittleEndian, + StringRef Strings, SmallVectorImpl<char> &NewSymtab, + NonRelocatableStringpool &NewStrings, + bool &InDebugNote) { + // Do not transfer undefined symbols, we want real addresses. + if ((NList.n_type & MachO::N_TYPE) == MachO::N_UNDF) + return false; + + StringRef Name = StringRef(Strings.begin() + NList.n_strx); + if (InDebugNote) { + InDebugNote = + (NList.n_type != MachO::N_SO) || (!Name.empty() && Name[0] != '\0'); + return false; + } else if (NList.n_type == MachO::N_SO) { + InDebugNote = true; + return false; + } + + // FIXME: The + 1 is here to mimic dsymutil-classic that has 2 empty + // strings at the start of the generated string table (There is + // corresponding code in the string table emission). + NList.n_strx = NewStrings.getStringOffset(Name) + 1; + if (IsLittleEndian != sys::IsLittleEndianHost) + MachO::swapStruct(NList); + + NewSymtab.append(reinterpret_cast<char *>(&NList), + reinterpret_cast<char *>(&NList + 1)); + return true; +} + +// Wrapper around transferSymbol to transfer all of \a Obj symbols +// to \a NewSymtab. This function does not write in the output file. +// \returns the number of symbols in \a NewSymtab. +static unsigned transferSymbols(const object::MachOObjectFile &Obj, + SmallVectorImpl<char> &NewSymtab, + NonRelocatableStringpool &NewStrings) { + unsigned Syms = 0; + StringRef Strings = Obj.getStringTableData(); + bool IsLittleEndian = Obj.isLittleEndian(); + bool InDebugNote = false; + + if (Obj.is64Bit()) { + for (const object::SymbolRef &Symbol : Obj.symbols()) { + object::DataRefImpl DRI = Symbol.getRawDataRefImpl(); + if (transferSymbol(Obj.getSymbol64TableEntry(DRI), IsLittleEndian, + Strings, NewSymtab, NewStrings, InDebugNote)) + ++Syms; + } + } else { + for (const object::SymbolRef &Symbol : Obj.symbols()) { + object::DataRefImpl DRI = Symbol.getRawDataRefImpl(); + if (transferSymbol(Obj.getSymbolTableEntry(DRI), IsLittleEndian, Strings, + NewSymtab, NewStrings, InDebugNote)) + ++Syms; + } + } + return Syms; +} + +static MachO::section +getSection(const object::MachOObjectFile &Obj, + const MachO::segment_command &Seg, + const object::MachOObjectFile::LoadCommandInfo &LCI, unsigned Idx) { + return Obj.getSection(LCI, Idx); +} + +static MachO::section_64 +getSection(const object::MachOObjectFile &Obj, + const MachO::segment_command_64 &Seg, + const object::MachOObjectFile::LoadCommandInfo &LCI, unsigned Idx) { + return Obj.getSection64(LCI, Idx); +} + +// Transfer \a Segment from \a Obj to the output file. This calls into \a Writer +// to write these load commands directly in the output file at the current +// position. +// The function also tries to find a hole in the address map to fit the __DWARF +// segment of \a DwarfSegmentSize size. \a EndAddress is updated to point at the +// highest segment address. +// When the __LINKEDIT segment is transfered, its offset and size are set resp. +// to \a LinkeditOffset and \a LinkeditSize. +template <typename SegmentTy> +static void transferSegmentAndSections( + const object::MachOObjectFile::LoadCommandInfo &LCI, SegmentTy Segment, + const object::MachOObjectFile &Obj, MCObjectWriter &Writer, + uint64_t LinkeditOffset, uint64_t LinkeditSize, uint64_t DwarfSegmentSize, + uint64_t &GapForDwarf, uint64_t &EndAddress) { + if (StringRef("__DWARF") == Segment.segname) + return; + + Segment.fileoff = Segment.filesize = 0; + + if (StringRef("__LINKEDIT") == Segment.segname) { + Segment.fileoff = LinkeditOffset; + Segment.filesize = LinkeditSize; + } + + // Check if the end address of the last segment and our current + // start address leave a sufficient gap to store the __DWARF + // segment. + uint64_t PrevEndAddress = EndAddress; + EndAddress = RoundUpToAlignment(EndAddress, 0x1000); + if (GapForDwarf == UINT64_MAX && Segment.vmaddr > EndAddress && + Segment.vmaddr - EndAddress >= DwarfSegmentSize) + GapForDwarf = EndAddress; + + // The segments are not necessarily sorted by their vmaddr. + EndAddress = + std::max<uint64_t>(PrevEndAddress, Segment.vmaddr + Segment.vmsize); + unsigned nsects = Segment.nsects; + if (Obj.isLittleEndian() != sys::IsLittleEndianHost) + MachO::swapStruct(Segment); + Writer.writeBytes( + StringRef(reinterpret_cast<char *>(&Segment), sizeof(Segment))); + for (unsigned i = 0; i < nsects; ++i) { + auto Sect = getSection(Obj, Segment, LCI, i); + Sect.offset = Sect.reloff = Sect.nreloc = 0; + if (Obj.isLittleEndian() != sys::IsLittleEndianHost) + MachO::swapStruct(Sect); + Writer.writeBytes(StringRef(reinterpret_cast<char *>(&Sect), sizeof(Sect))); + } +} + +// Write the __DWARF segment load command to the output file. +static void createDwarfSegment(uint64_t VMAddr, uint64_t FileOffset, + uint64_t FileSize, unsigned NumSections, + MCAsmLayout &Layout, MachObjectWriter &Writer) { + Writer.writeSegmentLoadCommand("__DWARF", NumSections, VMAddr, + RoundUpToAlignment(FileSize, 0x1000), + FileOffset, FileSize, /* MaxProt */ 7, + /* InitProt =*/3); + + for (unsigned int i = 0, n = Layout.getSectionOrder().size(); i != n; ++i) { + MCSection *Sec = Layout.getSectionOrder()[i]; + if (Sec->begin() == Sec->end() || !Layout.getSectionFileSize(Sec)) + continue; + + unsigned Align = Sec->getAlignment(); + if (Align > 1) { + VMAddr = RoundUpToAlignment(VMAddr, Align); + FileOffset = RoundUpToAlignment(FileOffset, Align); + } + Writer.writeSection(Layout, *Sec, VMAddr, FileOffset, 0, 0, 0); + + FileOffset += Layout.getSectionAddressSize(Sec); + VMAddr += Layout.getSectionAddressSize(Sec); + } +} + +static bool isExecutable(const object::MachOObjectFile &Obj) { + if (Obj.is64Bit()) + return Obj.getHeader64().filetype != MachO::MH_OBJECT; + else + return Obj.getHeader().filetype != MachO::MH_OBJECT; +} + +static bool hasLinkEditSegment(const object::MachOObjectFile &Obj) { + bool HasLinkEditSegment = false; + iterateOnSegments(Obj, [&](const MachO::segment_command_64 &Segment) { + if (StringRef("__LINKEDIT") == Segment.segname) + HasLinkEditSegment = true; + }); + return HasLinkEditSegment; +} + +static unsigned segmentLoadCommandSize(bool Is64Bit, unsigned NumSections) { + if (Is64Bit) + return sizeof(MachO::segment_command_64) + + NumSections * sizeof(MachO::section_64); + + return sizeof(MachO::segment_command) + NumSections * sizeof(MachO::section); +} + +// Stream a dSYM companion binary file corresponding to the binary referenced +// by \a DM to \a OutFile. The passed \a MS MCStreamer is setup to write to +// \a OutFile and it must be using a MachObjectWriter object to do so. +bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS, + raw_fd_ostream &OutFile) { + auto &ObjectStreamer = static_cast<MCObjectStreamer &>(MS); + MCAssembler &MCAsm = ObjectStreamer.getAssembler(); + auto &Writer = static_cast<MachObjectWriter &>(MCAsm.getWriter()); + MCAsmLayout Layout(MCAsm); + + MCAsm.layout(Layout); + + BinaryHolder InputBinaryHolder(false); + auto ErrOrObjs = InputBinaryHolder.GetObjectFiles(DM.getBinaryPath()); + if (auto Error = ErrOrObjs.getError()) + return error(Twine("opening ") + DM.getBinaryPath() + ": " + + Error.message(), + "output file streaming"); + + auto ErrOrInputBinary = + InputBinaryHolder.GetAs<object::MachOObjectFile>(DM.getTriple()); + if (auto Error = ErrOrInputBinary.getError()) + return error(Twine("opening ") + DM.getBinaryPath() + ": " + + Error.message(), + "output file streaming"); + auto &InputBinary = *ErrOrInputBinary; + + bool Is64Bit = Writer.is64Bit(); + MachO::symtab_command SymtabCmd = InputBinary.getSymtabLoadCommand(); + + // Get UUID. + MachO::uuid_command UUIDCmd; + memset(&UUIDCmd, 0, sizeof(UUIDCmd)); + UUIDCmd.cmd = MachO::LC_UUID; + UUIDCmd.cmdsize = sizeof(MachO::uuid_command); + for (auto &LCI : InputBinary.load_commands()) { + if (LCI.C.cmd == MachO::LC_UUID) { + UUIDCmd = InputBinary.getUuidCommand(LCI); + break; + } + } + + // Compute the number of load commands we will need. + unsigned LoadCommandSize = 0; + unsigned NumLoadCommands = 0; + // We will copy the UUID if there is one. + if (UUIDCmd.cmd != 0) { + ++NumLoadCommands; + LoadCommandSize += sizeof(MachO::uuid_command); + } + + // If we have a valid symtab to copy, do it. + bool ShouldEmitSymtab = + isExecutable(InputBinary) && hasLinkEditSegment(InputBinary); + if (ShouldEmitSymtab) { + LoadCommandSize += sizeof(MachO::symtab_command); + ++NumLoadCommands; + } + + unsigned HeaderSize = + Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); + // We will copy every segment that isn't __DWARF. + iterateOnSegments(InputBinary, [&](const MachO::segment_command_64 &Segment) { + if (StringRef("__DWARF") == Segment.segname) + return; + + ++NumLoadCommands; + LoadCommandSize += segmentLoadCommandSize(Is64Bit, Segment.nsects); + }); + + // We will add our own brand new __DWARF segment if we have debug + // info. + unsigned NumDwarfSections = 0; + uint64_t DwarfSegmentSize = 0; + + for (unsigned int i = 0, n = Layout.getSectionOrder().size(); i != n; ++i) { + MCSection *Sec = Layout.getSectionOrder()[i]; + if (Sec->begin() == Sec->end()) + continue; + + if (uint64_t Size = Layout.getSectionFileSize(Sec)) { + DwarfSegmentSize = + RoundUpToAlignment(DwarfSegmentSize, Sec->getAlignment()); + DwarfSegmentSize += Size; + ++NumDwarfSections; + } + } + + if (NumDwarfSections) { + ++NumLoadCommands; + LoadCommandSize += segmentLoadCommandSize(Is64Bit, NumDwarfSections); + } + + SmallString<0> NewSymtab; + NonRelocatableStringpool NewStrings; + unsigned NListSize = Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist); + unsigned NumSyms = 0; + uint64_t NewStringsSize = 0; + if (ShouldEmitSymtab) { + NewSymtab.reserve(SymtabCmd.nsyms * NListSize / 2); + NumSyms = transferSymbols(InputBinary, NewSymtab, NewStrings); + NewStringsSize = NewStrings.getSize() + 1; + } + + uint64_t SymtabStart = LoadCommandSize; + SymtabStart += HeaderSize; + SymtabStart = RoundUpToAlignment(SymtabStart, 0x1000); + + // We gathered all the information we need, start emitting the output file. + Writer.writeHeader(MachO::MH_DSYM, NumLoadCommands, LoadCommandSize, false); + + // Write the load commands. + assert(OutFile.tell() == HeaderSize); + if (UUIDCmd.cmd != 0) { + Writer.write32(UUIDCmd.cmd); + Writer.write32(UUIDCmd.cmdsize); + Writer.writeBytes( + StringRef(reinterpret_cast<const char *>(UUIDCmd.uuid), 16)); + assert(OutFile.tell() == HeaderSize + sizeof(UUIDCmd)); + } + + assert(SymtabCmd.cmd && "No symbol table."); + uint64_t StringStart = SymtabStart + NumSyms * NListSize; + if (ShouldEmitSymtab) + Writer.writeSymtabLoadCommand(SymtabStart, NumSyms, StringStart, + NewStringsSize); + + uint64_t DwarfSegmentStart = StringStart + NewStringsSize; + DwarfSegmentStart = RoundUpToAlignment(DwarfSegmentStart, 0x1000); + + // Write the load commands for the segments and sections we 'import' from + // the original binary. + uint64_t EndAddress = 0; + uint64_t GapForDwarf = UINT64_MAX; + for (auto &LCI : InputBinary.load_commands()) { + if (LCI.C.cmd == MachO::LC_SEGMENT) + transferSegmentAndSections(LCI, InputBinary.getSegmentLoadCommand(LCI), + InputBinary, Writer, SymtabStart, + StringStart + NewStringsSize - SymtabStart, + DwarfSegmentSize, GapForDwarf, EndAddress); + else if (LCI.C.cmd == MachO::LC_SEGMENT_64) + transferSegmentAndSections(LCI, InputBinary.getSegment64LoadCommand(LCI), + InputBinary, Writer, SymtabStart, + StringStart + NewStringsSize - SymtabStart, + DwarfSegmentSize, GapForDwarf, EndAddress); + } + + uint64_t DwarfVMAddr = RoundUpToAlignment(EndAddress, 0x1000); + uint64_t DwarfVMMax = Is64Bit ? UINT64_MAX : UINT32_MAX; + if (DwarfVMAddr + DwarfSegmentSize > DwarfVMMax || + DwarfVMAddr + DwarfSegmentSize < DwarfVMAddr /* Overflow */) { + // There is no room for the __DWARF segment at the end of the + // address space. Look trhough segments to find a gap. + DwarfVMAddr = GapForDwarf; + if (DwarfVMAddr == UINT64_MAX) + warn("not enough VM space for the __DWARF segment.", + "output file streaming"); + } + + // Write the load command for the __DWARF segment. + createDwarfSegment(DwarfVMAddr, DwarfSegmentStart, DwarfSegmentSize, + NumDwarfSections, Layout, Writer); + + assert(OutFile.tell() == LoadCommandSize + HeaderSize); + Writer.WriteZeros(SymtabStart - (LoadCommandSize + HeaderSize)); + assert(OutFile.tell() == SymtabStart); + + // Transfer symbols. + if (ShouldEmitSymtab) { + Writer.writeBytes(NewSymtab.str()); + assert(OutFile.tell() == StringStart); + + // Transfer string table. + // FIXME: The NonRelocatableStringpool starts with an empty string, but + // dsymutil-classic starts the reconstructed string table with 2 of these. + // Reproduce that behavior for now (there is corresponding code in + // transferSymbol). + Writer.WriteZeros(1); + typedef NonRelocatableStringpool::MapTy MapTy; + for (auto *Entry = NewStrings.getFirstEntry(); Entry; + Entry = static_cast<MapTy::MapEntryTy *>(Entry->getValue().second)) + Writer.writeBytes( + StringRef(Entry->getKey().data(), Entry->getKey().size() + 1)); + } + + assert(OutFile.tell() == StringStart + NewStringsSize); + + // Pad till the Dwarf segment start. + Writer.WriteZeros(DwarfSegmentStart - (StringStart + NewStringsSize)); + assert(OutFile.tell() == DwarfSegmentStart); + + // Emit the Dwarf sections contents. + for (const MCSection &Sec : MCAsm) { + if (Sec.begin() == Sec.end()) + continue; + + uint64_t Pos = OutFile.tell(); + Writer.WriteZeros(RoundUpToAlignment(Pos, Sec.getAlignment()) - Pos); + MCAsm.writeSectionData(&Sec, Layout); + } + + return true; +} +} +} +} diff --git a/tools/dsymutil/MachOUtils.h b/tools/dsymutil/MachOUtils.h new file mode 100644 index 000000000000..61dfadc70270 --- /dev/null +++ b/tools/dsymutil/MachOUtils.h @@ -0,0 +1,39 @@ +//===-- MachOUtils.h - Mach-o specific helpers for dsymutil --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H +#define LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H + +#include <string> +#include "llvm/ADT/StringRef.h" + +namespace llvm { +class MCStreamer; +class raw_fd_ostream; +namespace dsymutil { +class DebugMap; +struct LinkOptions; +namespace MachOUtils { + +struct ArchAndFilename { + std::string Arch, Path; + ArchAndFilename(StringRef Arch, StringRef Path) : Arch(Arch), Path(Path) {} +}; + +bool generateUniversalBinary(SmallVectorImpl<ArchAndFilename> &ArchFiles, + StringRef OutputFileName, const LinkOptions &, + StringRef SDKPath); + +bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS, + raw_fd_ostream &OutFile); + +std::string getArchName(StringRef Arch); +} +} +} +#endif // LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H diff --git a/tools/dsymutil/NonRelocatableStringpool.h b/tools/dsymutil/NonRelocatableStringpool.h new file mode 100644 index 000000000000..9db1f3342bd3 --- /dev/null +++ b/tools/dsymutil/NonRelocatableStringpool.h @@ -0,0 +1,68 @@ +//===-- NonRelocatableStringpool.h - A simple stringpool -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_DSYMUTIL_NONRELOCATABLESTRINGPOOL_H +#define LLVM_TOOLS_DSYMUTIL_NONRELOCATABLESTRINGPOOL_H + +namespace llvm { +namespace dsymutil { + +/// \brief A string table that doesn't need relocations. +/// +/// We are doing a final link, no need for a string table that +/// has relocation entries for every reference to it. This class +/// provides this ablitity by just associating offsets with +/// strings. +class NonRelocatableStringpool { +public: + /// \brief Entries are stored into the StringMap and simply linked + /// together through the second element of this pair in order to + /// keep track of insertion order. + typedef StringMap<std::pair<uint32_t, StringMapEntryBase *>, BumpPtrAllocator> + MapTy; + + NonRelocatableStringpool() + : CurrentEndOffset(0), Sentinel(0), Last(&Sentinel) { + // Legacy dsymutil puts an empty string at the start of the line + // table. + getStringOffset(""); + } + + /// \brief Get the offset of string \p S in the string table. This + /// can insert a new element or return the offset of a preexisitng + /// one. + uint32_t getStringOffset(StringRef S); + + /// \brief Get permanent storage for \p S (but do not necessarily + /// emit \p S in the output section). + /// \returns The StringRef that points to permanent storage to use + /// in place of \p S. + StringRef internString(StringRef S); + + // \brief Return the first entry of the string table. + const MapTy::MapEntryTy *getFirstEntry() const { + return getNextEntry(&Sentinel); + } + + // \brief Get the entry following \p E in the string table or null + // if \p E was the last entry. + const MapTy::MapEntryTy *getNextEntry(const MapTy::MapEntryTy *E) const { + return static_cast<const MapTy::MapEntryTy *>(E->getValue().second); + } + + uint64_t getSize() { return CurrentEndOffset; } + +private: + MapTy Strings; + uint32_t CurrentEndOffset; + MapTy::MapEntryTy Sentinel, *Last; +}; +} +} + +#endif diff --git a/tools/dsymutil/dsymutil.cpp b/tools/dsymutil/dsymutil.cpp index 50091935a44e..e9ee57f3dee6 100644 --- a/tools/dsymutil/dsymutil.cpp +++ b/tools/dsymutil/dsymutil.cpp @@ -13,13 +13,18 @@ //===----------------------------------------------------------------------===// #include "DebugMap.h" +#include "MachOUtils.h" #include "dsymutil.h" +#include "llvm/Object/MachO.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FileUtilities.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/Options.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/TargetSelect.h" +#include <cstdint> #include <string> using namespace llvm::dsymutil; @@ -27,35 +32,205 @@ using namespace llvm::dsymutil; namespace { using namespace llvm::cl; -static opt<std::string> InputFile(Positional, desc("<input file>"), - init("a.out")); +OptionCategory DsymCategory("Specific Options"); +static opt<bool> Help("h", desc("Alias for -help"), Hidden); +static opt<bool> Version("v", desc("Alias for -version"), Hidden); + +static list<std::string> InputFiles(Positional, OneOrMore, + desc("<input files>"), cat(DsymCategory)); static opt<std::string> OutputFileOpt("o", desc("Specify the output file. default: <input file>.dwarf"), - value_desc("filename")); + value_desc("filename"), cat(DsymCategory)); static opt<std::string> OsoPrependPath( "oso-prepend-path", desc("Specify a directory to prepend to the paths of object files."), - value_desc("path")); + value_desc("path"), cat(DsymCategory)); + +static opt<bool> DumpStab( + "symtab", + desc("Dumps the symbol table found in executable or object file(s) and\n" + "exits."), + init(false), cat(DsymCategory)); +static alias DumpStabA("s", desc("Alias for --symtab"), aliasopt(DumpStab)); + +static opt<bool> FlatOut("flat", + desc("Produce a flat dSYM file (not a bundle)."), + init(false), cat(DsymCategory)); +static alias FlatOutA("f", desc("Alias for --flat"), aliasopt(FlatOut)); -static opt<bool> Verbose("v", desc("Verbosity level"), init(false)); +static opt<bool> Verbose("verbose", desc("Verbosity level"), init(false), + cat(DsymCategory)); static opt<bool> NoOutput("no-output", desc("Do the link in memory, but do not emit the result file."), - init(false)); + init(false), cat(DsymCategory)); + +static list<std::string> ArchFlags( + "arch", + desc("Link DWARF debug information only for specified CPU architecture\n" + "types. This option can be specified multiple times, once for each\n" + "desired architecture. All cpu architectures will be linked by\n" + "default."), + ZeroOrMore, cat(DsymCategory)); + +static opt<bool> + NoODR("no-odr", + desc("Do not use ODR (One Definition Rule) for type uniquing."), + init(false), cat(DsymCategory)); static opt<bool> DumpDebugMap( "dump-debug-map", desc("Parse and dump the debug map to standard output. Not DWARF link " "will take place."), - init(false)); + init(false), cat(DsymCategory)); static opt<bool> InputIsYAMLDebugMap( "y", desc("Treat the input file is a YAML debug map rather than a binary."), - init(false)); + init(false), cat(DsymCategory)); +} + +static bool createPlistFile(llvm::StringRef BundleRoot) { + if (NoOutput) + return true; + + // Create plist file to write to. + llvm::SmallString<128> InfoPlist(BundleRoot); + llvm::sys::path::append(InfoPlist, "Contents/Info.plist"); + std::error_code EC; + llvm::raw_fd_ostream PL(InfoPlist, EC, llvm::sys::fs::F_Text); + if (EC) { + llvm::errs() << "error: cannot create plist file " << InfoPlist << ": " + << EC.message() << '\n'; + return false; + } + + // FIXME: Use CoreFoundation to get executable bundle info. Use + // dummy values for now. + std::string bundleVersionStr = "1", bundleShortVersionStr = "1.0", + bundleIDStr; + + llvm::StringRef BundleID = *llvm::sys::path::rbegin(BundleRoot); + if (llvm::sys::path::extension(BundleRoot) == ".dSYM") + bundleIDStr = llvm::sys::path::stem(BundleID); + else + bundleIDStr = BundleID; + + // Print out information to the plist file. + PL << "<?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n" + << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" " + << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" + << "<plist version=\"1.0\">\n" + << "\t<dict>\n" + << "\t\t<key>CFBundleDevelopmentRegion</key>\n" + << "\t\t<string>English</string>\n" + << "\t\t<key>CFBundleIdentifier</key>\n" + << "\t\t<string>com.apple.xcode.dsym." << bundleIDStr << "</string>\n" + << "\t\t<key>CFBundleInfoDictionaryVersion</key>\n" + << "\t\t<string>6.0</string>\n" + << "\t\t<key>CFBundlePackageType</key>\n" + << "\t\t<string>dSYM</string>\n" + << "\t\t<key>CFBundleSignature</key>\n" + << "\t\t<string>\?\?\?\?</string>\n" + << "\t\t<key>CFBundleShortVersionString</key>\n" + << "\t\t<string>" << bundleShortVersionStr << "</string>\n" + << "\t\t<key>CFBundleVersion</key>\n" + << "\t\t<string>" << bundleVersionStr << "</string>\n" + << "\t</dict>\n" + << "</plist>\n"; + + PL.close(); + return true; +} + +static bool createBundleDir(llvm::StringRef BundleBase) { + if (NoOutput) + return true; + + llvm::SmallString<128> Bundle(BundleBase); + llvm::sys::path::append(Bundle, "Contents", "Resources", "DWARF"); + if (std::error_code EC = create_directories(Bundle.str(), true, + llvm::sys::fs::perms::all_all)) { + llvm::errs() << "error: cannot create directory " << Bundle << ": " + << EC.message() << "\n"; + return false; + } + return true; +} + +static std::error_code getUniqueFile(const llvm::Twine &Model, int &ResultFD, + llvm::SmallVectorImpl<char> &ResultPath) { + // If in NoOutput mode, use the createUniqueFile variant that + // doesn't open the file but still generates a somewhat unique + // name. In the real usage scenario, we'll want to ensure that the + // file is trully unique, and creating it is the only way to achieve + // that. + if (NoOutput) + return llvm::sys::fs::createUniqueFile(Model, ResultPath); + return llvm::sys::fs::createUniqueFile(Model, ResultFD, ResultPath); +} + +static std::string getOutputFileName(llvm::StringRef InputFile, + bool TempFile = false) { + if (TempFile) { + llvm::StringRef Basename = + OutputFileOpt.empty() ? InputFile : llvm::StringRef(OutputFileOpt); + llvm::Twine OutputFile = Basename + ".tmp%%%%%%.dwarf"; + int FD; + llvm::SmallString<128> UniqueFile; + if (auto EC = getUniqueFile(OutputFile, FD, UniqueFile)) { + llvm::errs() << "error: failed to create temporary outfile '" + << OutputFile << "': " << EC.message() << '\n'; + return ""; + } + llvm::sys::RemoveFileOnSignal(UniqueFile); + if (!NoOutput) { + // Close the file immediately. We know it is unique. It will be + // reopened and written to later. + llvm::raw_fd_ostream CloseImmediately(FD, true /* shouldClose */, true); + } + return UniqueFile.str(); + } + + if (FlatOut) { + // If a flat dSYM has been requested, things are pretty simple. + if (OutputFileOpt.empty()) { + if (InputFile == "-") + return "a.out.dwarf"; + return (InputFile + ".dwarf").str(); + } + + return OutputFileOpt; + } + + // We need to create/update a dSYM bundle. + // A bundle hierarchy looks like this: + // <bundle name>.dSYM/ + // Contents/ + // Info.plist + // Resources/ + // DWARF/ + // <DWARF file(s)> + std::string DwarfFile = + InputFile == "-" ? llvm::StringRef("a.out") : InputFile; + llvm::SmallString<128> BundleDir(OutputFileOpt); + if (BundleDir.empty()) + BundleDir = DwarfFile + ".dSYM"; + if (!createBundleDir(BundleDir) || !createPlistFile(BundleDir)) + return ""; + + llvm::sys::path::append(BundleDir, "Contents", "Resources", "DWARF", + llvm::sys::path::filename(DwarfFile)); + return BundleDir.str(); +} + +void llvm::dsymutil::exitDsymutil(int ExitStatus) { + // Cleanup temporary files. + llvm::sys::RunInterruptHandlers(); + exit(ExitStatus); } int main(int argc, char **argv) { @@ -63,41 +238,105 @@ int main(int argc, char **argv) { llvm::PrettyStackTraceProgram StackPrinter(argc, argv); llvm::llvm_shutdown_obj Shutdown; LinkOptions Options; + void *MainAddr = (void *)(intptr_t)&exitDsymutil; + std::string SDKPath = llvm::sys::fs::getMainExecutable(argv[0], MainAddr); + SDKPath = llvm::sys::path::parent_path(SDKPath); - llvm::cl::ParseCommandLineOptions(argc, argv, "llvm dsymutil\n"); + HideUnrelatedOptions(DsymCategory); + llvm::cl::ParseCommandLineOptions( + argc, argv, + "manipulate archived DWARF debug symbol files.\n\n" + "dsymutil links the DWARF debug information found in the object files\n" + "for the executable <input file> by using debug symbols information\n" + "contained in its symbol table.\n"); - auto DebugMapPtrOrErr = - parseDebugMap(InputFile, OsoPrependPath, Verbose, InputIsYAMLDebugMap); + if (Help) + PrintHelpMessage(); + + if (Version) { + llvm::cl::PrintVersionMessage(); + return 0; + } Options.Verbose = Verbose; Options.NoOutput = NoOutput; + Options.NoODR = NoODR; + Options.PrependPath = OsoPrependPath; llvm::InitializeAllTargetInfos(); llvm::InitializeAllTargetMCs(); llvm::InitializeAllTargets(); llvm::InitializeAllAsmPrinters(); - if (auto EC = DebugMapPtrOrErr.getError()) { - llvm::errs() << "error: cannot parse the debug map for \"" << InputFile - << "\": " << EC.message() << '\n'; + if (!FlatOut && OutputFileOpt == "-") { + llvm::errs() << "error: cannot emit to standard output without --flat\n"; + return 1; + } + + if (InputFiles.size() > 1 && FlatOut && !OutputFileOpt.empty()) { + llvm::errs() << "error: cannot use -o with multiple inputs in flat mode\n"; return 1; } - if (Verbose || DumpDebugMap) - (*DebugMapPtrOrErr)->print(llvm::outs()); + for (const auto &Arch : ArchFlags) + if (Arch != "*" && Arch != "all" && + !llvm::object::MachOObjectFile::isValidArch(Arch)) { + llvm::errs() << "error: Unsupported cpu architecture: '" << Arch << "'\n"; + exitDsymutil(1); + } - if (DumpDebugMap) - return 0; + for (auto &InputFile : InputFiles) { + // Dump the symbol table for each input file and requested arch + if (DumpStab) { + if (!dumpStab(InputFile, ArchFlags, OsoPrependPath)) + exitDsymutil(1); + continue; + } + + auto DebugMapPtrsOrErr = parseDebugMap(InputFile, ArchFlags, OsoPrependPath, + Verbose, InputIsYAMLDebugMap); + + if (auto EC = DebugMapPtrsOrErr.getError()) { + llvm::errs() << "error: cannot parse the debug map for \"" << InputFile + << "\": " << EC.message() << '\n'; + exitDsymutil(1); + } + + if (DebugMapPtrsOrErr->empty()) { + llvm::errs() << "error: no architecture to link\n"; + exitDsymutil(1); + } + + // If there is more than one link to execute, we need to generate + // temporary files. + bool NeedsTempFiles = !DumpDebugMap && (*DebugMapPtrsOrErr).size() != 1; + llvm::SmallVector<MachOUtils::ArchAndFilename, 4> TempFiles; + for (auto &Map : *DebugMapPtrsOrErr) { + if (Verbose || DumpDebugMap) + Map->print(llvm::outs()); + + if (DumpDebugMap) + continue; + + if (Map->begin() == Map->end()) + llvm::errs() << "warning: no debug symbols in executable (-arch " + << MachOUtils::getArchName(Map->getTriple().getArchName()) + << ")\n"; + + std::string OutputFile = getOutputFileName(InputFile, NeedsTempFiles); + if (OutputFile.empty() || !linkDwarf(OutputFile, *Map, Options)) + exitDsymutil(1); + + if (NeedsTempFiles) + TempFiles.emplace_back(Map->getTriple().getArchName().str(), + OutputFile); + } - std::string OutputFile; - if (OutputFileOpt.empty()) { - if (InputFile == "-") - OutputFile = "a.out.dwarf"; - else - OutputFile = InputFile + ".dwarf"; - } else { - OutputFile = OutputFileOpt; + if (NeedsTempFiles && + !MachOUtils::generateUniversalBinary( + TempFiles, getOutputFileName(InputFile), Options, SDKPath)) + exitDsymutil(1); } - return !linkDwarf(OutputFile, **DebugMapPtrOrErr, Options); + exitDsymutil(0); } diff --git a/tools/dsymutil/dsymutil.h b/tools/dsymutil/dsymutil.h index 408918779833..91cb32766129 100644 --- a/tools/dsymutil/dsymutil.h +++ b/tools/dsymutil/dsymutil.h @@ -27,22 +27,35 @@ namespace dsymutil { struct LinkOptions { bool Verbose; ///< Verbosity bool NoOutput; ///< Skip emitting output + bool NoODR; ///< Do not unique types according to ODR + std::string PrependPath; ///< -oso-prepend-path LinkOptions() : Verbose(false), NoOutput(false) {} }; -/// \brief Extract the DebugMap from the given file. -/// The file has to be a MachO object file. -llvm::ErrorOr<std::unique_ptr<DebugMap>> parseDebugMap(StringRef InputFile, - StringRef PrependPath, - bool Verbose, - bool InputIsYAML); +/// \brief Extract the DebugMaps from the given file. +/// The file has to be a MachO object file. Multiple debug maps can be +/// returned when the file is universal (aka fat) binary. +llvm::ErrorOr<std::vector<std::unique_ptr<DebugMap>>> +parseDebugMap(StringRef InputFile, ArrayRef<std::string> Archs, + StringRef PrependPath, bool Verbose, bool InputIsYAML); + +/// \brief Dump the symbol table +bool dumpStab(StringRef InputFile, ArrayRef<std::string> Archs, + StringRef PrependPath = ""); /// \brief Link the Dwarf debuginfo as directed by the passed DebugMap /// \p DM into a DwarfFile named \p OutputFilename. /// \returns false if the link failed. bool linkDwarf(StringRef OutputFilename, const DebugMap &DM, const LinkOptions &Options); + +/// \brief Exit the dsymutil process, cleaning up every temporary +/// files that we created. +LLVM_ATTRIBUTE_NORETURN void exitDsymutil(int ExitStatus); + +void warn(const Twine &Warning, const Twine &Context); +bool error(const Twine &Error, const Twine &Context); } } #endif // LLVM_TOOLS_DSYMUTIL_DSYMUTIL_H diff --git a/tools/gold/gold-plugin.cpp b/tools/gold/gold-plugin.cpp index 68c9d1a6f6e4..31d7f156a700 100644 --- a/tools/gold/gold-plugin.cpp +++ b/tools/gold/gold-plugin.cpp @@ -20,6 +20,7 @@ #include "llvm/Bitcode/ReaderWriter.h" #include "llvm/CodeGen/Analysis.h" #include "llvm/CodeGen/CommandFlags.h" +#include "llvm/CodeGen/ParallelCG.h" #include "llvm/IR/AutoUpgrade.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DiagnosticInfo.h" @@ -28,15 +29,16 @@ #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" #include "llvm/IR/Verifier.h" -#include "llvm/Linker/Linker.h" +#include "llvm/Linker/IRMover.h" #include "llvm/MC/SubtargetFeature.h" +#include "llvm/Object/FunctionIndexObjectFile.h" #include "llvm/Object/IRObjectFile.h" -#include "llvm/Support/raw_ostream.h" #include "llvm/Support/Host.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" +#include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/IPO.h" #include "llvm/Transforms/IPO/PassManagerBuilder.h" #include "llvm/Transforms/Utils/GlobalStatus.h" @@ -55,30 +57,59 @@ using namespace llvm; +static ld_plugin_status discard_message(int level, const char *format, ...) { + // Die loudly. Recent versions of Gold pass ld_plugin_message as the first + // callback in the transfer vector. This should never be called. + abort(); +} + +static ld_plugin_release_input_file release_input_file = nullptr; +static ld_plugin_get_input_file get_input_file = nullptr; +static ld_plugin_message message = discard_message; + namespace { struct claimed_file { void *handle; std::vector<ld_plugin_symbol> syms; }; -} -static ld_plugin_status discard_message(int level, const char *format, ...) { - // Die loudly. Recent versions of Gold pass ld_plugin_message as the first - // callback in the transfer vector. This should never be called. - abort(); +/// RAII wrapper to manage opening and releasing of a ld_plugin_input_file. +struct PluginInputFile { + void *Handle; + ld_plugin_input_file File; + + PluginInputFile(void *Handle) : Handle(Handle) { + if (get_input_file(Handle, &File) != LDPS_OK) + message(LDPL_FATAL, "Failed to get file information"); + } + ~PluginInputFile() { + if (release_input_file(Handle) != LDPS_OK) + message(LDPL_FATAL, "Failed to release file information"); + } + ld_plugin_input_file &file() { return File; } +}; + +struct ResolutionInfo { + bool IsLinkonceOdr = true; + bool UnnamedAddr = true; + GlobalValue::VisibilityTypes Visibility = GlobalValue::DefaultVisibility; + bool CommonInternal = false; + bool UseCommon = false; + unsigned CommonSize = 0; + unsigned CommonAlign = 0; + claimed_file *CommonFile = nullptr; +}; } -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 Reloc::Model RelocationModel = Reloc::Default; static std::string output_name = ""; static std::list<claimed_file> Modules; +static StringMap<ResolutionInfo> ResInfo; static std::vector<std::string> Cleanup; static llvm::TargetOptions TargetOpts; @@ -92,10 +123,20 @@ namespace options { static bool generate_api_file = false; static OutputType TheOutputType = OT_NORMAL; static unsigned OptLevel = 2; + static unsigned Parallelism = 1; +#ifdef NDEBUG + static bool DisableVerify = true; +#else + static bool DisableVerify = false; +#endif static std::string obj_path; static std::string extra_library_path; static std::string triple; static std::string mcpu; + // When the thinlto plugin option is specified, only read the function + // the information from intermediate files and write a combined + // global index for the ThinLTO backends. + static bool thinlto = false; // Additional options to pass into the code generator. // Note: This array will contain all plugin options which are not claimed // as plugin exclusive to pass to the code generator. @@ -103,7 +144,7 @@ namespace options { // use only and will not be passed. static std::vector<const char *> extra; - static void process_plugin_option(const char* opt_) + static void process_plugin_option(const char *opt_) { if (opt_ == nullptr) return; @@ -125,10 +166,17 @@ namespace options { TheOutputType = OT_SAVE_TEMPS; } else if (opt == "disable-output") { TheOutputType = OT_DISABLE; + } else if (opt == "thinlto") { + thinlto = true; } else if (opt.size() == 2 && opt[0] == 'O') { if (opt[1] < '0' || opt[1] > '3') - report_fatal_error("Optimization level must be between 0 and 3"); + message(LDPL_FATAL, "Optimization level must be between 0 and 3"); OptLevel = opt[1] - '0'; + } else if (opt.startswith("jobs=")) { + if (StringRef(opt_ + 5).getAsInteger(10, Parallelism)) + message(LDPL_FATAL, "Invalid parallelism level: %s", opt_ + 5); + } else if (opt == "disable-verify") { + DisableVerify = true; } else { // Save this option to pass to the code generator. // ParseCommandLineOptions() expects argv[0] to be program name. Lazily @@ -278,7 +326,7 @@ static bool shouldSkip(uint32_t Symflags) { return false; } -static void diagnosticHandler(const DiagnosticInfo &DI, void *Context) { +static void diagnosticHandler(const DiagnosticInfo &DI) { if (const auto *BDI = dyn_cast<BitcodeDiagnosticInfo>(&DI)) { std::error_code EC = BDI->getError(); if (EC == BitcodeError::InvalidBitcodeSignature) @@ -308,6 +356,23 @@ static void diagnosticHandler(const DiagnosticInfo &DI, void *Context) { message(Level, "LLVM gold plugin: %s", ErrStorage.c_str()); } +static void diagnosticHandlerForContext(const DiagnosticInfo &DI, + void *Context) { + diagnosticHandler(DI); +} + +static GlobalValue::VisibilityTypes +getMinVisibility(GlobalValue::VisibilityTypes A, + GlobalValue::VisibilityTypes B) { + if (A == GlobalValue::HiddenVisibility) + return A; + if (B == GlobalValue::HiddenVisibility) + return B; + if (A == GlobalValue::ProtectedVisibility) + return A; + return B; +} + /// 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. @@ -322,7 +387,8 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file, message(LDPL_ERROR, "Failed to get a view of %s", file->name); return LDPS_ERR; } - BufferRef = MemoryBufferRef(StringRef((const char *)view, file->filesize), ""); + 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 @@ -341,7 +407,7 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file, BufferRef = Buffer->getMemBufferRef(); } - Context.setDiagnosticHandler(diagnosticHandler); + Context.setDiagnosticHandler(diagnosticHandlerForContext); ErrorOr<std::unique_ptr<object::IRObjectFile>> ObjOrErr = object::IRObjectFile::create(BufferRef, Context); std::error_code EC = ObjOrErr.getError(); @@ -363,6 +429,11 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file, cf.handle = file->handle; + // If we are doing ThinLTO compilation, don't need to process the symbols. + // Later we simply build a combined index file after all files are claimed. + if (options::thinlto) + return LDPS_OK; + for (auto &Sym : Obj->symbols()) { uint32_t Symflags = Sym.getFlags(); if (shouldSkip(Symflags)) @@ -381,8 +452,22 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file, const GlobalValue *GV = Obj->getSymbolGV(Sym.getRawDataRefImpl()); + ResolutionInfo &Res = ResInfo[sym.name]; + sym.visibility = LDPV_DEFAULT; if (GV) { + Res.UnnamedAddr &= GV->hasUnnamedAddr(); + Res.IsLinkonceOdr &= GV->hasLinkOnceLinkage(); + if (GV->hasCommonLinkage()) { + Res.CommonAlign = std::max(Res.CommonAlign, GV->getAlignment()); + const DataLayout &DL = GV->getParent()->getDataLayout(); + uint64_t Size = DL.getTypeAllocSize(GV->getType()->getElementType()); + if (Size >= Res.CommonSize) { + Res.CommonSize = Size; + Res.CommonFile = &cf; + } + } + Res.Visibility = getMinVisibility(Res.Visibility, GV->getVisibility()); switch (GV->getVisibility()) { case GlobalValue::DefaultVisibility: sym.visibility = LDPV_DEFAULT; @@ -421,15 +506,13 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file, 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; } if (!cf.syms.empty()) { - if (add_symbols(cf.handle, cf.syms.size(), &cf.syms[0]) != LDPS_OK) { + if (add_symbols(cf.handle, cf.syms.size(), cf.syms.data()) != LDPS_OK) { message(LDPL_ERROR, "Unable to add symbols!"); return LDPS_ERR; } @@ -438,27 +521,6 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file, 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; - } - - assert(!GV.isDiscardableIfUnused()); -} - static void internalize(GlobalValue &GV) { if (GV.isDeclarationForLinker()) return; // We get here if there is a matching asm definition. @@ -466,33 +528,6 @@ static void internalize(GlobalValue &GV) { GV.setLinkage(GlobalValue::InternalLinkage); } -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(); -} - static const char *getResolutionName(ld_plugin_symbol_resolution R) { switch (R) { case LDPR_UNKNOWN: @@ -519,71 +554,51 @@ static const char *getResolutionName(ld_plugin_symbol_resolution R) { llvm_unreachable("Unknown resolution"); } -namespace { -class LocalValueMaterializer : public ValueMaterializer { - DenseSet<GlobalValue *> &Dropped; - DenseMap<GlobalObject *, GlobalObject *> LocalVersions; - -public: - LocalValueMaterializer(DenseSet<GlobalValue *> &Dropped) : Dropped(Dropped) {} - Value *materializeValueFor(Value *V) override; -}; +static void freeSymName(ld_plugin_symbol &Sym) { + free(Sym.name); + free(Sym.comdat_key); + Sym.name = nullptr; + Sym.comdat_key = nullptr; } -Value *LocalValueMaterializer::materializeValueFor(Value *V) { - auto *GO = dyn_cast<GlobalObject>(V); - if (!GO) - return nullptr; +static std::unique_ptr<FunctionInfoIndex> +getFunctionIndexForFile(claimed_file &F, ld_plugin_input_file &Info) { - auto I = LocalVersions.find(GO); - if (I != LocalVersions.end()) - return I->second; + if (get_symbols(F.handle, F.syms.size(), &F.syms[0]) != LDPS_OK) + message(LDPL_FATAL, "Failed to get symbol information"); - if (!Dropped.count(GO)) - return nullptr; + const void *View; + if (get_view(F.handle, &View) != LDPS_OK) + message(LDPL_FATAL, "Failed to get a view of file"); - Module &M = *GO->getParent(); - GlobalValue::LinkageTypes L = GO->getLinkage(); - GlobalObject *Declaration; - if (auto *F = dyn_cast<Function>(GO)) { - Declaration = Function::Create(F->getFunctionType(), L, "", &M); - } else { - auto *Var = cast<GlobalVariable>(GO); - Declaration = new GlobalVariable(M, Var->getType()->getElementType(), - Var->isConstant(), L, - /*Initializer*/ nullptr); - } - Declaration->takeName(GO); - Declaration->copyAttributesFrom(GO); + MemoryBufferRef BufferRef(StringRef((const char *)View, Info.filesize), + Info.name); - GO->setLinkage(GlobalValue::InternalLinkage); - GO->setName(Declaration->getName()); - Dropped.erase(GO); - GO->replaceAllUsesWith(Declaration); + // Don't bother trying to build an index if there is no summary information + // in this bitcode file. + if (!object::FunctionIndexObjectFile::hasFunctionSummaryInMemBuffer( + BufferRef, diagnosticHandler)) + return std::unique_ptr<FunctionInfoIndex>(nullptr); - LocalVersions[Declaration] = GO; + ErrorOr<std::unique_ptr<object::FunctionIndexObjectFile>> ObjOrErr = + object::FunctionIndexObjectFile::create(BufferRef, diagnosticHandler); - return GO; -} + if (std::error_code EC = ObjOrErr.getError()) + message(LDPL_FATAL, "Could not read function index bitcode from file : %s", + EC.message().c_str()); -static Constant *mapConstantToLocalCopy(Constant *C, ValueToValueMapTy &VM, - LocalValueMaterializer *Materializer) { - return MapValue(C, VM, RF_IgnoreMissingEntries, nullptr, Materializer); -} + object::FunctionIndexObjectFile &Obj = **ObjOrErr; -static void freeSymName(ld_plugin_symbol &Sym) { - free(Sym.name); - free(Sym.comdat_key); - Sym.name = nullptr; - Sym.comdat_key = nullptr; + return Obj.takeIndex(); } static std::unique_ptr<Module> getModuleForFile(LLVMContext &Context, claimed_file &F, ld_plugin_input_file &Info, raw_fd_ostream *ApiFile, - StringSet<> &Internalize, StringSet<> &Maybe) { + StringSet<> &Internalize, StringSet<> &Maybe, + std::vector<GlobalValue *> &Keep) { - if (get_symbols(F.handle, F.syms.size(), &F.syms[0]) != LDPS_OK) + if (get_symbols(F.handle, F.syms.size(), F.syms.data()) != LDPS_OK) message(LDPL_FATAL, "Failed to get symbol information"); const void *View; @@ -609,11 +624,12 @@ getModuleForFile(LLVMContext &Context, claimed_file &F, SmallPtrSet<GlobalValue *, 8> Used; collectUsedGlobalVariables(M, Used, /*CompilerUsed*/ false); - DenseSet<GlobalValue *> Drop; - std::vector<GlobalAlias *> KeptAliases; - unsigned SymNum = 0; for (auto &ObjSym : Obj.symbols()) { + GlobalValue *GV = Obj.getSymbolGV(ObjSym.getRawDataRefImpl()); + if (GV && GV->hasAppendingLinkage()) + Keep.push_back(GV); + if (shouldSkip(ObjSym.getFlags())) continue; ld_plugin_symbol &Sym = F.syms[SymNum]; @@ -625,20 +641,37 @@ getModuleForFile(LLVMContext &Context, claimed_file &F, if (options::generate_api_file) *ApiFile << Sym.name << ' ' << getResolutionName(Resolution) << '\n'; - GlobalValue *GV = Obj.getSymbolGV(ObjSym.getRawDataRefImpl()); if (!GV) { freeSymName(Sym); 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. - freeSymName(Sym); - continue; + ResolutionInfo &Res = ResInfo[Sym.name]; + if (Resolution == LDPR_PREVAILING_DEF_IRONLY_EXP && !Res.IsLinkonceOdr) + Resolution = LDPR_PREVAILING_DEF; + + GV->setUnnamedAddr(Res.UnnamedAddr); + GV->setVisibility(Res.Visibility); + + // Override gold's resolution for common symbols. We want the largest + // one to win. + if (GV->hasCommonLinkage()) { + cast<GlobalVariable>(GV)->setAlignment(Res.CommonAlign); + if (Resolution == LDPR_PREVAILING_DEF_IRONLY) + Res.CommonInternal = true; + + if (Resolution == LDPR_PREVAILING_DEF_IRONLY || + Resolution == LDPR_PREVAILING_DEF) + Res.UseCommon = true; + + if (Res.CommonFile == &F && Res.UseCommon) { + if (Res.CommonInternal) + Resolution = LDPR_PREVAILING_DEF_IRONLY; + else + Resolution = LDPR_PREVAILING_DEF; + } else { + Resolution = LDPR_PREEMPTED_IR; + } } switch (Resolution) { @@ -648,40 +681,37 @@ getModuleForFile(LLVMContext &Context, claimed_file &F, case LDPR_RESOLVED_IR: case LDPR_RESOLVED_EXEC: case LDPR_RESOLVED_DYN: - assert(GV->isDeclarationForLinker()); + case LDPR_PREEMPTED_IR: + case LDPR_PREEMPTED_REG: break; case LDPR_UNDEF: - if (!GV->isDeclarationForLinker()) { + if (!GV->isDeclarationForLinker()) assert(GV->hasComdat()); - Drop.insert(GV); - } 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. + Keep.push_back(GV); + // The IR linker has to be able to map this value to a declaration, + // so we can only internalize after linking. + if (!Used.count(GV)) Internalize.insert(GV->getName()); - } 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(GV->getName()); - - // fall-through - case LDPR_PREEMPTED_REG: - Drop.insert(GV); + Keep.push_back(GV); + // There is a non IR use, so we have to force optimizations to keep this. + switch (GV->getLinkage()) { + default: + break; + case GlobalValue::LinkOnceAnyLinkage: + GV->setLinkage(GlobalValue::WeakAnyLinkage); + break; + case GlobalValue::LinkOnceODRLinkage: + GV->setLinkage(GlobalValue::WeakODRLinkage); + break; + } break; case LDPR_PREVAILING_DEF_IRONLY_EXP: { @@ -689,9 +719,8 @@ getModuleForFile(LLVMContext &Context, claimed_file &F, // 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(GV->getName()); - keepGlobalValue(*GV, KeptAliases); + Maybe.insert(GV->getName()); + Keep.push_back(GV); break; } } @@ -699,25 +728,11 @@ getModuleForFile(LLVMContext &Context, claimed_file &F, freeSymName(Sym); } - 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); - GA->setAliasee(Replacement); - } - - for (auto *GV : Drop) - drop(*GV); - return Obj.takeModule(); } static void runLTOPasses(Module &M, TargetMachine &TM) { - if (const DataLayout *DL = TM.getDataLayout()) - M.setDataLayout(*DL); + M.setDataLayout(TM.createDataLayout()); legacy::PassManager passes; passes.add(createTargetTransformInfoWrapperPass(TM.getTargetIRAnalysis())); @@ -725,8 +740,10 @@ static void runLTOPasses(Module &M, TargetMachine &TM) { PassManagerBuilder PMB; PMB.LibraryInfo = new TargetLibraryInfoImpl(Triple(TM.getTargetTriple())); PMB.Inliner = createFunctionInliningPass(); + // Unconditionally verify input since it is not verified before this + // point and has unknown origin. PMB.VerifyInput = true; - PMB.VerifyOutput = true; + PMB.VerifyOutput = !options::DisableVerify; PMB.LoopVectorize = true; PMB.SLPVectorize = true; PMB.OptLevel = options::OptLevel; @@ -739,11 +756,11 @@ static void saveBCFile(StringRef Path, Module &M) { 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, /* ShouldPreserveUseListOrder */ true); + WriteBitcodeToFile(&M, OS, /* ShouldPreserveUseListOrder */ false); } -static void codegen(Module &M) { - const std::string &TripleStr = M.getTargetTriple(); +static void codegen(std::unique_ptr<Module> M) { + const std::string &TripleStr = M->getTargetTriple(); Triple TheTriple(TripleStr); std::string ErrMsg; @@ -779,12 +796,10 @@ static void codegen(Module &M) { TripleStr, options::mcpu, Features.getString(), Options, RelocationModel, CodeModel::Default, CGOptLevel)); - runLTOPasses(M, *TM); + runLTOPasses(*M, *TM); if (options::TheOutputType == options::OT_SAVE_TEMPS) - saveBCFile(output_name + ".opt.bc", M); - - legacy::PassManager CodeGenPasses; + saveBCFile(output_name + ".opt.bc", *M); SmallString<128> Filename; if (!options::obj_path.empty()) @@ -792,37 +807,47 @@ static void codegen(Module &M) { else if (options::TheOutputType == options::OT_SAVE_TEMPS) Filename = output_name + ".o"; - int FD; + std::vector<SmallString<128>> Filenames(options::Parallelism); bool TempOutFile = Filename.empty(); - if (TempOutFile) { - 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 { - 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()); - } - { - raw_fd_ostream OS(FD, true); + // Open a file descriptor for each backend thread. This is done in a block + // so that the output file descriptors are closed before gold opens them. + std::list<llvm::raw_fd_ostream> OSs; + std::vector<llvm::raw_pwrite_stream *> OSPtrs(options::Parallelism); + for (unsigned I = 0; I != options::Parallelism; ++I) { + int FD; + if (TempOutFile) { + std::error_code EC = + sys::fs::createTemporaryFile("lto-llvm", "o", FD, Filenames[I]); + if (EC) + message(LDPL_FATAL, "Could not create temporary file: %s", + EC.message().c_str()); + } else { + Filenames[I] = Filename; + if (options::Parallelism != 1) + Filenames[I] += utostr(I); + std::error_code EC = + sys::fs::openFileForWrite(Filenames[I], FD, sys::fs::F_None); + if (EC) + message(LDPL_FATAL, "Could not open file: %s", EC.message().c_str()); + } + OSs.emplace_back(FD, true); + OSPtrs[I] = &OSs.back(); + } - if (TM->addPassesToEmitFile(CodeGenPasses, OS, - TargetMachine::CGFT_ObjectFile)) - message(LDPL_FATAL, "Failed to setup codegen"); - CodeGenPasses.run(M); + // Run backend threads. + splitCodeGen(std::move(M), OSPtrs, options::mcpu, Features.getString(), + Options, RelocationModel, CodeModel::Default, CGOptLevel); } - 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 (TempOutFile) - Cleanup.push_back(Filename.c_str()); + for (auto &Filename : Filenames) { + 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 (TempOutFile) + Cleanup.push_back(Filename.c_str()); + } } /// gold informs us that all symbols have been read. At this point, we use @@ -832,32 +857,58 @@ static ld_plugin_status allSymbolsReadHook(raw_fd_ostream *ApiFile) { if (Modules.empty()) return LDPS_OK; + // If we are doing ThinLTO compilation, simply build the combined + // function index/summary and emit it. We don't need to parse the modules + // and link them in this case. + if (options::thinlto) { + FunctionInfoIndex CombinedIndex; + uint64_t NextModuleId = 0; + for (claimed_file &F : Modules) { + PluginInputFile InputFile(F.handle); + + std::unique_ptr<FunctionInfoIndex> Index = + getFunctionIndexForFile(F, InputFile.file()); + + // Skip files without a function summary. + if (Index) + CombinedIndex.mergeFrom(std::move(Index), ++NextModuleId); + } + + std::error_code EC; + raw_fd_ostream OS(output_name + ".thinlto.bc", EC, + sys::fs::OpenFlags::F_None); + if (EC) + message(LDPL_FATAL, "Unable to open %s.thinlto.bc for writing: %s", + output_name.data(), EC.message().c_str()); + WriteFunctionSummaryToFile(CombinedIndex, OS); + OS.close(); + + cleanup_hook(); + exit(0); + } + LLVMContext Context; - Context.setDiagnosticHandler(diagnosticHandler, nullptr, true); + Context.setDiagnosticHandler(diagnosticHandlerForContext, nullptr, true); std::unique_ptr<Module> Combined(new Module("ld-temp.o", Context)); - Linker L(Combined.get()); + IRMover L(*Combined); std::string DefaultTriple = sys::getDefaultTargetTriple(); StringSet<> Internalize; StringSet<> Maybe; for (claimed_file &F : Modules) { - ld_plugin_input_file File; - if (get_input_file(F.handle, &File) != LDPS_OK) - message(LDPL_FATAL, "Failed to get file information"); - std::unique_ptr<Module> M = - getModuleForFile(Context, F, File, ApiFile, Internalize, Maybe); + PluginInputFile InputFile(F.handle); + std::vector<GlobalValue *> Keep; + std::unique_ptr<Module> M = getModuleForFile( + Context, F, InputFile.file(), ApiFile, Internalize, Maybe, Keep); if (!options::triple.empty()) M->setTargetTriple(options::triple.c_str()); - else if (M->getTargetTriple().empty()) { + else if (M->getTargetTriple().empty()) M->setTargetTriple(DefaultTriple); - } - if (L.linkInModule(M.get())) + if (L.move(*M, Keep, [](GlobalValue &, IRMover::ValueAdder) {})) message(LDPL_FATAL, "Failed to link module"); - if (release_input_file(F.handle) != LDPS_OK) - message(LDPL_FATAL, "Failed to release file information"); } for (const auto &Name : Internalize) { @@ -884,12 +935,12 @@ static ld_plugin_status allSymbolsReadHook(raw_fd_ostream *ApiFile) { path = output_name; else path = output_name + ".bc"; - saveBCFile(path, *L.getModule()); + saveBCFile(path, *Combined); if (options::TheOutputType == options::OT_BC_ONLY) return LDPS_OK; } - codegen(*L.getModule()); + codegen(std::move(Combined)); if (!options::extra_library_path.empty() && set_extra_library_path(options::extra_library_path.c_str()) != LDPS_OK) diff --git a/tools/llc/CMakeLists.txt b/tools/llc/CMakeLists.txt index ff5a89e1da44..136cf4be1c12 100644 --- a/tools/llc/CMakeLists.txt +++ b/tools/llc/CMakeLists.txt @@ -11,6 +11,7 @@ set(LLVM_LINK_COMPONENTS SelectionDAG Support Target + TransformUtils ) # Support plugins. diff --git a/tools/llc/LLVMBuild.txt b/tools/llc/LLVMBuild.txt index 38660cf27a46..c1f5cebea859 100644 --- a/tools/llc/LLVMBuild.txt +++ b/tools/llc/LLVMBuild.txt @@ -19,4 +19,4 @@ type = Tool name = llc parent = Tools -required_libraries = AsmParser BitReader IRReader MIRParser all-targets +required_libraries = AsmParser BitReader IRReader MIRParser TransformUtils all-targets diff --git a/tools/llc/Makefile b/tools/llc/Makefile index ae64c9a5b57c..cd34c80d840a 100644 --- a/tools/llc/Makefile +++ b/tools/llc/Makefile @@ -9,7 +9,7 @@ LEVEL := ../.. TOOLNAME := llc -LINK_COMPONENTS := all-targets bitreader asmparser irreader mirparser +LINK_COMPONENTS := all-targets bitreader asmparser irreader mirparser transformutils # Support plugins. NO_DEAD_STRIP := 1 diff --git a/tools/llc/llc.cpp b/tools/llc/llc.cpp index e33cd795d3ae..bffa39fd9e5c 100644 --- a/tools/llc/llc.cpp +++ b/tools/llc/llc.cpp @@ -45,6 +45,7 @@ #include "llvm/Support/ToolOutputFile.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Target/TargetSubtargetInfo.h" +#include "llvm/Transforms/Utils/Cloning.h" #include <memory> using namespace llvm; @@ -96,6 +97,12 @@ static cl::opt<bool> AsmVerbose("asm-verbose", cl::desc("Add comments to directives."), cl::init(true)); +static cl::opt<bool> + CompileTwice("compile-twice", cl::Hidden, + cl::desc("Run everything twice, re-using the same pass " + "manager and verify the result is the same."), + cl::init(false)); + static int compileModule(char **, LLVMContext &); static std::unique_ptr<tool_output_file> @@ -312,8 +319,7 @@ static int compileModule(char **argv, LLVMContext &Context) { PM.add(new TargetLibraryInfoWrapperPass(TLII)); // Add the target data from the target machine, if it exists, or the module. - if (const DataLayout *DL = Target->getDataLayout()) - M->setDataLayout(*DL); + M->setDataLayout(Target->createDataLayout()); // Override function attributes based on CPUStr, FeaturesStr, and command line // flags. @@ -326,10 +332,15 @@ static int compileModule(char **argv, LLVMContext &Context) { { raw_pwrite_stream *OS = &Out->os(); - std::unique_ptr<buffer_ostream> BOS; - if (FileType != TargetMachine::CGFT_AssemblyFile && - !Out->os().supportsSeeking()) { - BOS = make_unique<buffer_ostream>(*OS); + + // Manually do the buffering rather than using buffer_ostream, + // so we can memcmp the contents in CompileTwice mode + SmallVector<char, 0> Buffer; + std::unique_ptr<raw_svector_ostream> BOS; + if ((FileType != TargetMachine::CGFT_AssemblyFile && + !Out->os().supportsSeeking()) || + CompileTwice) { + BOS = make_unique<raw_svector_ostream>(Buffer); OS = BOS.get(); } @@ -379,7 +390,39 @@ static int compileModule(char **argv, LLVMContext &Context) { // Before executing passes, print the final values of the LLVM options. cl::PrintOptionValues(); + // If requested, run the pass manager over the same module again, + // to catch any bugs due to persistent state in the passes. Note that + // opt has the same functionality, so it may be worth abstracting this out + // in the future. + SmallVector<char, 0> CompileTwiceBuffer; + if (CompileTwice) { + std::unique_ptr<Module> M2(llvm::CloneModule(M.get())); + PM.run(*M2); + CompileTwiceBuffer = Buffer; + Buffer.clear(); + } + PM.run(*M); + + // Compare the two outputs and make sure they're the same + if (CompileTwice) { + if (Buffer.size() != CompileTwiceBuffer.size() || + (memcmp(Buffer.data(), CompileTwiceBuffer.data(), Buffer.size()) != + 0)) { + errs() + << "Running the pass manager twice changed the output.\n" + "Writing the result of the second run to the specified output\n" + "To generate the one-run comparison binary, just run without\n" + "the compile-twice option\n"; + Out->os() << Buffer; + Out->keep(); + return 1; + } + } + + if (BOS) { + Out->os() << Buffer; + } } // Declare success. diff --git a/tools/lli/OrcLazyJIT.cpp b/tools/lli/OrcLazyJIT.cpp index ae276e6cda8f..4235145ee7a5 100644 --- a/tools/lli/OrcLazyJIT.cpp +++ b/tools/lli/OrcLazyJIT.cpp @@ -38,26 +38,39 @@ namespace { "Dump modules to the current " "working directory. (WARNING: " "will overwrite existing files)."), - clEnumValEnd)); + clEnumValEnd), + cl::Hidden); + + cl::opt<bool> OrcInlineStubs("orc-lazy-inline-stubs", + cl::desc("Try to inline stubs"), + cl::init(true), cl::Hidden); } -OrcLazyJIT::CallbackManagerBuilder -OrcLazyJIT::createCallbackManagerBuilder(Triple T) { +std::unique_ptr<OrcLazyJIT::CompileCallbackMgr> +OrcLazyJIT::createCompileCallbackMgr(Triple T) { switch (T.getArch()) { default: return nullptr; case Triple::x86_64: { - typedef orc::JITCompileCallbackManager<IRDumpLayerT, - orc::OrcX86_64> CCMgrT; - return [](IRDumpLayerT &IRDumpLayer, RuntimeDyld::MemoryManager &MemMgr, - LLVMContext &Context) { - return llvm::make_unique<CCMgrT>(IRDumpLayer, MemMgr, Context, 0, - 64); - }; + typedef orc::LocalJITCompileCallbackManager<orc::OrcX86_64> CCMgrT; + return llvm::make_unique<CCMgrT>(0); } } } +OrcLazyJIT::IndirectStubsManagerBuilder +OrcLazyJIT::createIndirectStubsMgrBuilder(Triple T) { + switch (T.getArch()) { + default: return nullptr; + + case Triple::x86_64: + return [](){ + return llvm::make_unique< + orc::LocalIndirectStubsManager<orc::OrcX86_64>>(); + }; + } +} + OrcLazyJIT::TransformFtor OrcLazyJIT::createDebugDumper() { switch (OrcDumpKind) { @@ -111,6 +124,12 @@ OrcLazyJIT::TransformFtor OrcLazyJIT::createDebugDumper() { // Defined in lli.cpp. CodeGenOpt::Level getOptLevel(); + +template <typename PtrTy> +static PtrTy fromTargetAddress(orc::TargetAddress Addr) { + return reinterpret_cast<PtrTy>(static_cast<uintptr_t>(Addr)); +} + int llvm::runOrcLazyJIT(std::unique_ptr<Module> M, int ArgC, char* ArgV[]) { // Add the program's symbols into the JIT's search space. if (sys::DynamicLibrary::LoadLibraryPermanently(nullptr)) { @@ -123,20 +142,31 @@ int llvm::runOrcLazyJIT(std::unique_ptr<Module> M, int ArgC, char* ArgV[]) { EngineBuilder EB; EB.setOptLevel(getOptLevel()); auto TM = std::unique_ptr<TargetMachine>(EB.selectTarget()); - auto &Context = getGlobalContext(); - auto CallbackMgrBuilder = - OrcLazyJIT::createCallbackManagerBuilder(Triple(TM->getTargetTriple())); + auto CompileCallbackMgr = + OrcLazyJIT::createCompileCallbackMgr(Triple(TM->getTargetTriple())); // If we couldn't build the factory function then there must not be a callback // manager for this target. Bail out. - if (!CallbackMgrBuilder) { + if (!CompileCallbackMgr) { errs() << "No callback manager available for target '" << TM->getTargetTriple().str() << "'.\n"; return 1; } + auto IndirectStubsMgrBuilder = + OrcLazyJIT::createIndirectStubsMgrBuilder(Triple(TM->getTargetTriple())); + + // If we couldn't build a stubs-manager-builder for this target then bail out. + if (!IndirectStubsMgrBuilder) { + errs() << "No indirect stubs manager available for target '" + << TM->getTargetTriple().str() << "'.\n"; + return 1; + } + // Everything looks good. Build the JIT. - OrcLazyJIT J(std::move(TM), Context, CallbackMgrBuilder); + OrcLazyJIT J(std::move(TM), std::move(CompileCallbackMgr), + std::move(IndirectStubsMgrBuilder), + OrcInlineStubs); // Add the module, look up main and run it. auto MainHandle = J.addModule(std::move(M)); @@ -148,6 +178,6 @@ int llvm::runOrcLazyJIT(std::unique_ptr<Module> M, int ArgC, char* ArgV[]) { } typedef int (*MainFnPtr)(int, char*[]); - auto Main = OrcLazyJIT::fromTargetAddress<MainFnPtr>(MainSym.getAddress()); + auto Main = fromTargetAddress<MainFnPtr>(MainSym.getAddress()); return Main(ArgC, ArgV); } diff --git a/tools/lli/OrcLazyJIT.h b/tools/lli/OrcLazyJIT.h index fe86adbf9516..bb4da33ea9b6 100644 --- a/tools/lli/OrcLazyJIT.h +++ b/tools/lli/OrcLazyJIT.h @@ -23,39 +23,36 @@ #include "llvm/ExecutionEngine/Orc/IRTransformLayer.h" #include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h" #include "llvm/ExecutionEngine/RTDyldMemoryManager.h" -#include "llvm/IR/LLVMContext.h" namespace llvm { class OrcLazyJIT { public: - typedef orc::JITCompileCallbackManagerBase CompileCallbackMgr; + typedef orc::JITCompileCallbackManager CompileCallbackMgr; typedef orc::ObjectLinkingLayer<> ObjLayerT; typedef orc::IRCompileLayer<ObjLayerT> CompileLayerT; typedef std::function<std::unique_ptr<Module>(std::unique_ptr<Module>)> TransformFtor; typedef orc::IRTransformLayer<CompileLayerT, TransformFtor> IRDumpLayerT; typedef orc::CompileOnDemandLayer<IRDumpLayerT, CompileCallbackMgr> CODLayerT; + typedef CODLayerT::IndirectStubsManagerBuilderT + IndirectStubsManagerBuilder; typedef CODLayerT::ModuleSetHandleT ModuleHandleT; - typedef std::function< - std::unique_ptr<CompileCallbackMgr>(IRDumpLayerT&, - RuntimeDyld::MemoryManager&, - LLVMContext&)> - CallbackManagerBuilder; - - static CallbackManagerBuilder createCallbackManagerBuilder(Triple T); - - OrcLazyJIT(std::unique_ptr<TargetMachine> TM, LLVMContext &Context, - CallbackManagerBuilder &BuildCallbackMgr) - : TM(std::move(TM)), - ObjectLayer(), - CompileLayer(ObjectLayer, orc::SimpleCompiler(*this->TM)), - IRDumpLayer(CompileLayer, createDebugDumper()), - CCMgr(BuildCallbackMgr(IRDumpLayer, CCMgrMemMgr, Context)), - CODLayer(IRDumpLayer, *CCMgr, false), - CXXRuntimeOverrides([this](const std::string &S) { return mangle(S); }) {} + OrcLazyJIT(std::unique_ptr<TargetMachine> TM, + std::unique_ptr<CompileCallbackMgr> CCMgr, + IndirectStubsManagerBuilder IndirectStubsMgrBuilder, + bool InlineStubs) + : TM(std::move(TM)), DL(this->TM->createDataLayout()), + CCMgr(std::move(CCMgr)), + ObjectLayer(), + CompileLayer(ObjectLayer, orc::SimpleCompiler(*this->TM)), + IRDumpLayer(CompileLayer, createDebugDumper()), + CODLayer(IRDumpLayer, extractSingleFunction, *this->CCMgr, + std::move(IndirectStubsMgrBuilder), InlineStubs), + CXXRuntimeOverrides( + [this](const std::string &S) { return mangle(S); }) {} ~OrcLazyJIT() { // Run any destructors registered with __cxa_atexit. @@ -65,15 +62,13 @@ public: DtorRunner.runViaLayer(CODLayer); } - template <typename PtrTy> - static PtrTy fromTargetAddress(orc::TargetAddress Addr) { - return reinterpret_cast<PtrTy>(static_cast<uintptr_t>(Addr)); - } + static std::unique_ptr<CompileCallbackMgr> createCompileCallbackMgr(Triple T); + static IndirectStubsManagerBuilder createIndirectStubsMgrBuilder(Triple T); ModuleHandleT addModule(std::unique_ptr<Module> M) { // Attach a data-layout if one isn't already present. if (M->getDataLayout().isDefault()) - M->setDataLayout(*TM->getDataLayout()); + M->setDataLayout(DL); // Record the static constructors and destructors. We have to do this before // we hand over ownership of the module to the JIT. @@ -136,20 +131,27 @@ private: std::string MangledName; { raw_string_ostream MangledNameStream(MangledName); - Mangler::getNameWithPrefix(MangledNameStream, Name, *TM->getDataLayout()); + Mangler::getNameWithPrefix(MangledNameStream, Name, DL); } return MangledName; } + static std::set<Function*> extractSingleFunction(Function &F) { + std::set<Function*> Partition; + Partition.insert(&F); + return Partition; + } + static TransformFtor createDebugDumper(); std::unique_ptr<TargetMachine> TM; + DataLayout DL; SectionMemoryManager CCMgrMemMgr; + std::unique_ptr<CompileCallbackMgr> CCMgr; ObjLayerT ObjectLayer; CompileLayerT CompileLayer; IRDumpLayerT IRDumpLayer; - std::unique_ptr<CompileCallbackMgr> CCMgr; CODLayerT CODLayer; orc::LocalCXXRuntimeOverrides CXXRuntimeOverrides; diff --git a/tools/lli/RemoteTarget.cpp b/tools/lli/RemoteTarget.cpp index 850fdc506999..95e1511eaaf5 100644 --- a/tools/lli/RemoteTarget.cpp +++ b/tools/lli/RemoteTarget.cpp @@ -1,4 +1,4 @@ -//===- RemoteTarget.cpp - LLVM Remote process JIT execution --------------===// +//===- RemoteTarget.cpp - LLVM Remote process JIT execution -----*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -56,7 +56,7 @@ bool RemoteTarget::loadCode(uint64_t Address, const void *Data, size_t Size) { } bool RemoteTarget::executeCode(uint64_t Address, int &RetVal) { - int (*fn)(void) = (int(*)(void))Address; + int (*fn)() = (int(*)())Address; RetVal = fn(); return true; } diff --git a/tools/lli/lli.cpp b/tools/lli/lli.cpp index 057841f0ad60..9f714060c17a 100644 --- a/tools/lli/lli.cpp +++ b/tools/lli/lli.cpp @@ -262,8 +262,7 @@ public: if (!getCacheFilename(ModuleID, CacheName)) return; if (!CacheDir.empty()) { // Create user-defined cache dir. - SmallString<128> dir(CacheName); - sys::path::remove_filename(dir); + SmallString<128> dir(sys::path::parent_path(CacheName)); sys::fs::create_directories(Twine(dir)); } std::error_code EC; @@ -422,7 +421,7 @@ int main(int argc, char **argv, char * const *envp) { // If not jitting lazily, load the whole bitcode file eagerly too. if (NoLazyCompilation) { - if (std::error_code EC = Mod->materializeAllPermanently()) { + if (std::error_code EC = Mod->materializeAll()) { errs() << argv[0] << ": bitcode didn't read correctly.\n"; errs() << "Reason: " << EC.message() << "\n"; exit(1); diff --git a/tools/llvm-ar/CMakeLists.txt b/tools/llvm-ar/CMakeLists.txt index 05ffe36fa7fe..86233dfce9a5 100644 --- a/tools/llvm-ar/CMakeLists.txt +++ b/tools/llvm-ar/CMakeLists.txt @@ -10,30 +10,5 @@ add_llvm_tool(llvm-ar llvm-ar.cpp ) -if(UNIX) - set(LLVM_LINK_OR_COPY create_symlink) - set(llvm_ar_binary "llvm-ar${CMAKE_EXECUTABLE_SUFFIX}") -else() - set(LLVM_LINK_OR_COPY copy) - set(llvm_ar_binary "${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-ar${CMAKE_EXECUTABLE_SUFFIX}") -endif() - -set(llvm_ranlib "${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-ranlib${CMAKE_EXECUTABLE_SUFFIX}") - -add_custom_command(OUTPUT ${llvm_ranlib} - COMMAND ${CMAKE_COMMAND} -E ${LLVM_LINK_OR_COPY} "${llvm_ar_binary}" "${llvm_ranlib}" - DEPENDS llvm-ar) - -add_custom_target(llvm-ranlib ALL DEPENDS ${llvm_ranlib}) -set_target_properties(llvm-ranlib PROPERTIES FOLDER Tools) - -set(llvm_lib "${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-lib${CMAKE_EXECUTABLE_SUFFIX}") - -add_custom_command(OUTPUT ${llvm_lib} - COMMAND ${CMAKE_COMMAND} -E ${LLVM_LINK_OR_COPY} "${llvm_ar_binary}" "${llvm_lib}" - DEPENDS llvm-ar) - -add_custom_target(llvm-lib ALL DEPENDS ${llvm_lib}) -set_target_properties(llvm-lib PROPERTIES FOLDER Tools) - -install(SCRIPT install_symlink.cmake -DCMAKE_INSTALL_PREFIX=\"${CMAKE_INSTALL_PREFIX}\") +add_llvm_tool_symlink(llvm-ranlib llvm-ar) +add_llvm_tool_symlink(llvm-lib llvm-ar) diff --git a/tools/llvm-ar/install_symlink.cmake b/tools/llvm-ar/install_symlink.cmake deleted file mode 100644 index d48431b128de..000000000000 --- a/tools/llvm-ar/install_symlink.cmake +++ /dev/null @@ -1,31 +0,0 @@ -# We need to execute this script at installation time because the -# DESTDIR environment variable may be unset at configuration time. -# See PR8397. - -if(UNIX) - set(LINK_OR_COPY create_symlink) - set(DESTDIR $ENV{DESTDIR}) -else() - set(LINK_OR_COPY copy) -endif() - -# CMAKE_EXECUTABLE_SUFFIX is undefined on cmake scripts. See PR9286. -if( WIN32 ) - set(EXECUTABLE_SUFFIX ".exe") -else() - set(EXECUTABLE_SUFFIX "") -endif() - -set(bindir "${DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/") - -message("Creating llvm-ranlib") - -execute_process( - COMMAND "${CMAKE_COMMAND}" -E ${LINK_OR_COPY} "llvm-ar${EXECUTABLE_SUFFIX}" "llvm-ranlib${EXECUTABLE_SUFFIX}" - WORKING_DIRECTORY "${bindir}") - -message("Creating llvm-lib") - -execute_process( - COMMAND "${CMAKE_COMMAND}" -E ${LINK_OR_COPY} "llvm-ar${EXECUTABLE_SUFFIX}" "llvm-lib${EXECUTABLE_SUFFIX}" - WORKING_DIRECTORY "${bindir}") diff --git a/tools/llvm-ar/llvm-ar.cpp b/tools/llvm-ar/llvm-ar.cpp index 2c9668c63b8c..ef5fab68b947 100644 --- a/tools/llvm-ar/llvm-ar.cpp +++ b/tools/llvm-ar/llvm-ar.cpp @@ -81,7 +81,7 @@ static cl::opt<Format> clEnumValN(GNU, "gnu", "gnu"), clEnumValN(BSD, "bsd", "bsd"), clEnumValEnd)); -std::string Options; +static std::string Options; // Provide additional help output explaining the operations and modifiers of // llvm-ar. This object instructs the CommandLine library to print the text of @@ -130,6 +130,7 @@ static bool OnlyUpdate = false; ///< 'u' modifier static bool Verbose = false; ///< 'v' modifier static bool Symtab = true; ///< 's' modifier static bool Deterministic = true; ///< 'D' and 'U' modifiers +static bool Thin = false; ///< 'T' modifier // Relative Positional Argument (for insert/move). This variable holds // the name of the archive member to which the 'a', 'b' or 'i' modifier @@ -252,6 +253,9 @@ static ArchiveOperation parseCommandLine() { case 'U': Deterministic = false; break; + case 'T': + Thin = true; + break; default: cl::PrintHelpMessage(); } @@ -308,18 +312,9 @@ static void doPrint(StringRef Name, const object::Archive::Child &C) { // 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"; - else - outs() << "-"; - if (mode & 002) - outs() << "w"; - else - outs() << "-"; - if (mode & 001) - outs() << "x"; - else - outs() << "-"; + outs() << ((mode & 004) ? "r" : "-"); + outs() << ((mode & 002) ? "w" : "-"); + outs() << ((mode & 001) ? "x" : "-"); } // Implement the 't' operation. This function prints out just @@ -334,7 +329,9 @@ static void doDisplayTable(StringRef Name, const object::Archive::Child &C) { printMode(Mode & 007); outs() << ' ' << C.getUID(); outs() << '/' << C.getGID(); - outs() << ' ' << format("%6llu", C.getSize()); + ErrorOr<uint64_t> Size = C.getSize(); + failIfError(Size.getError()); + outs() << ' ' << format("%6llu", Size.get()); outs() << ' ' << C.getLastModified().str(); outs() << ' '; } @@ -393,13 +390,14 @@ static bool shouldCreateArchive(ArchiveOperation Op) { static void performReadOperation(ArchiveOperation Operation, object::Archive *OldArchive) { - if (Operation == Extract && OldArchive->isThin()) { - errs() << "extracting from a thin archive is not supported\n"; - std::exit(1); - } + if (Operation == Extract && OldArchive->isThin()) + fail("extracting from a thin archive is not supported"); bool Filter = !Members.empty(); - for (const object::Archive::Child &C : OldArchive->children()) { + for (auto &ChildOrErr : OldArchive->children()) { + failIfError(ChildOrErr.getError()); + const object::Archive::Child &C = *ChildOrErr; + ErrorOr<StringRef> NameOrErr = C.getName(); failIfError(NameOrErr.getError()); StringRef Name = NameOrErr.get(); @@ -432,10 +430,21 @@ static void performReadOperation(ArchiveOperation Operation, std::exit(1); } -template <typename T> -void addMember(std::vector<NewArchiveIterator> &Members, T I, StringRef Name, - int Pos = -1) { - NewArchiveIterator NI(I, Name); +static void addMember(std::vector<NewArchiveIterator> &Members, + StringRef FileName, int Pos = -1) { + NewArchiveIterator NI(FileName); + if (Pos == -1) + Members.push_back(NI); + else + Members[Pos] = NI; +} + +static void addMember(std::vector<NewArchiveIterator> &Members, + const object::Archive::Child &M, StringRef Name, + int Pos = -1) { + if (Thin && !M.getParent()->isThin()) + fail("Cannot convert a regular archive to a thin one"); + NewArchiveIterator NI(M, Name); if (Pos == -1) Members.push_back(NI); else @@ -451,7 +460,7 @@ enum InsertAction { }; static InsertAction computeInsertAction(ArchiveOperation Operation, - object::Archive::child_iterator I, + const object::Archive::Child &Member, StringRef Name, std::vector<StringRef>::iterator &Pos) { if (Operation == QuickAppend || Members.empty()) @@ -485,7 +494,7 @@ static InsertAction computeInsertAction(ArchiveOperation Operation, // operation. sys::fs::file_status Status; failIfError(sys::fs::status(*MI, Status), *MI); - if (Status.getLastModificationTime() < I->getLastModified()) { + if (Status.getLastModificationTime() < Member.getLastModified()) { if (PosName.empty()) return IA_AddOldMember; return IA_MoveOldMember; @@ -508,7 +517,9 @@ computeNewArchiveMembers(ArchiveOperation Operation, int InsertPos = -1; StringRef PosName = sys::path::filename(RelPos); if (OldArchive) { - for (auto &Child : OldArchive->children()) { + for (auto &ChildOrErr : OldArchive->children()) { + failIfError(ChildOrErr.getError()); + auto &Child = ChildOrErr.get(); int Pos = Ret.size(); ErrorOr<StringRef> NameOrErr = Child.getName(); failIfError(NameOrErr.getError()); @@ -529,7 +540,7 @@ computeNewArchiveMembers(ArchiveOperation Operation, addMember(Ret, Child, Name); break; case IA_AddNewMeber: - addMember(Ret, *MemberI, Name); + addMember(Ret, *MemberI); break; case IA_Delete: break; @@ -537,7 +548,7 @@ computeNewArchiveMembers(ArchiveOperation Operation, addMember(Moved, Child, Name); break; case IA_MoveNewMember: - addMember(Moved, *MemberI, Name); + addMember(Moved, *MemberI); break; } if (MemberI != Members.end()) @@ -557,12 +568,10 @@ computeNewArchiveMembers(ArchiveOperation Operation, assert(unsigned(InsertPos) <= Ret.size()); Ret.insert(Ret.begin() + InsertPos, Moved.begin(), Moved.end()); - Ret.insert(Ret.begin() + InsertPos, Members.size(), - NewArchiveIterator("", "")); + Ret.insert(Ret.begin() + InsertPos, Members.size(), NewArchiveIterator("")); int Pos = InsertPos; for (auto &Member : Members) { - StringRef Name = sys::path::filename(Member); - addMember(Ret, Member, Name, Pos); + addMember(Ret, Member, Pos); ++Pos; } @@ -590,15 +599,15 @@ performWriteOperation(ArchiveOperation Operation, object::Archive *OldArchive, break; } if (NewMembersP) { - std::pair<StringRef, std::error_code> Result = - writeArchive(ArchiveName, *NewMembersP, Symtab, Kind, Deterministic); + std::pair<StringRef, std::error_code> Result = writeArchive( + ArchiveName, *NewMembersP, Symtab, Kind, Deterministic, Thin); failIfError(Result.second, Result.first); return; } std::vector<NewArchiveIterator> NewMembers = computeNewArchiveMembers(Operation, OldArchive); auto Result = - writeArchive(ArchiveName, NewMembers, Symtab, Kind, Deterministic); + writeArchive(ArchiveName, NewMembers, Symtab, Kind, Deterministic, Thin); failIfError(Result.second, Result.first); } @@ -644,20 +653,13 @@ static int performOperation(ArchiveOperation Operation, ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = MemoryBuffer::getFile(ArchiveName, -1, false); std::error_code EC = Buf.getError(); - if (EC && EC != errc::no_such_file_or_directory) { - errs() << ToolName << ": error opening '" << ArchiveName - << "': " << EC.message() << "!\n"; - return 1; - } + if (EC && EC != errc::no_such_file_or_directory) + fail("error opening '" + ArchiveName + "': " + EC.message() + "!"); if (!EC) { object::Archive Archive(Buf.get()->getMemBufferRef(), EC); - - if (EC) { - errs() << ToolName << ": error loading '" << ArchiveName - << "': " << EC.message() << "!\n"; - return 1; - } + failIfError(EC, + "error loading '" + ArchiveName + "': " + EC.message() + "!"); performOperation(Operation, &Archive, NewMembers); return 0; } @@ -713,7 +715,9 @@ static void runMRIScript() { failIfError(LibOrErr.getError(), "Could not parse library"); Archives.push_back(std::move(*LibOrErr)); object::Archive &Lib = *Archives.back(); - for (auto &Member : Lib.children()) { + for (auto &MemberOrErr : Lib.children()) { + failIfError(MemberOrErr.getError()); + auto &Member = MemberOrErr.get(); ErrorOr<StringRef> NameOrErr = Member.getName(); failIfError(NameOrErr.getError()); addMember(NewMembers, Member, *NameOrErr); @@ -721,7 +725,7 @@ static void runMRIScript() { break; } case MRICommand::AddMod: - addMember(NewMembers, Rest, sys::path::filename(Rest)); + addMember(NewMembers, Rest); break; case MRICommand::Create: Create = true; @@ -784,9 +788,9 @@ int main(int argc, char **argv) { " This program archives bitcode files into single libraries\n" ); - if (Stem.find("ar") != StringRef::npos) - return ar_main(); if (Stem.find("ranlib") != StringRef::npos) return ranlib_main(); + if (Stem.find("ar") != StringRef::npos) + return ar_main(); fail("Not ranlib, ar or lib!"); } diff --git a/tools/llvm-as-fuzzer/CMakeLists.txt b/tools/llvm-as-fuzzer/CMakeLists.txt new file mode 100644 index 000000000000..ff9bdaaf4c43 --- /dev/null +++ b/tools/llvm-as-fuzzer/CMakeLists.txt @@ -0,0 +1,13 @@ +if( LLVM_USE_SANITIZE_COVERAGE ) + set(LLVM_LINK_COMPONENTS + AsmParser + BitWriter + Core + Support + ) + add_llvm_tool(llvm-as-fuzzer + llvm-as-fuzzer.cpp) + target_link_libraries(llvm-as-fuzzer + LLVMFuzzer + ) +endif() diff --git a/tools/llvm-as-fuzzer/llvm-as-fuzzer.cpp b/tools/llvm-as-fuzzer/llvm-as-fuzzer.cpp new file mode 100644 index 000000000000..b4024bcaa99a --- /dev/null +++ b/tools/llvm-as-fuzzer/llvm-as-fuzzer.cpp @@ -0,0 +1,76 @@ +//===--- fuzz-llvm-as.cpp - Fuzzer for llvm-as using lib/Fuzzer -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Build tool to fuzz the LLVM assembler (llvm-as) using +// lib/Fuzzer. The main reason for using this tool is that it is much +// faster than using afl-fuzz, since it is run in-process. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/StringRef.h" +#include "llvm/AsmParser/Parser.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Verifier.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/SourceMgr.h" + +#include <csetjmp> + +using namespace llvm; + +static jmp_buf JmpBuf; + +namespace { + +void MyFatalErrorHandler(void *user_data, const std::string& reason, + bool gen_crash_diag) { + // Don't bother printing reason, just return to the test function, + // since a fatal error represents a successful parse (i.e. it correctly + // terminated with an error message to the user). + longjmp(JmpBuf, 1); +} + +static bool InstalledHandler = false; + +} // end of anonymous namespace + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + + // Allocate space for locals before setjmp so that memory can be collected + // if parse exits prematurely (via longjmp). + StringRef Input((const char *)Data, Size); + // Note: We need to create a buffer to add a null terminator to the + // end of the input string. The parser assumes that the string + // parsed is always null terminated. + std::unique_ptr<MemoryBuffer> MemBuf = MemoryBuffer::getMemBufferCopy(Input); + SMDiagnostic Err; + LLVMContext &Context = getGlobalContext(); + std::unique_ptr<Module> M; + + if (setjmp(JmpBuf)) + // If reached, we have returned with non-zero status, so exit. + return 0; + + // TODO(kschimpf) Write a main to do this initialization. + if (!InstalledHandler) { + llvm::install_fatal_error_handler(::MyFatalErrorHandler, nullptr); + InstalledHandler = true; + } + + M = parseAssembly(MemBuf->getMemBufferRef(), Err, Context); + + if (!M.get()) + return 0; + + verifyModule(*M.get()); + return 0; +} diff --git a/tools/llvm-as/llvm-as.cpp b/tools/llvm-as/llvm-as.cpp index 4455d24fb60d..d4e4d8d71070 100644 --- a/tools/llvm-as/llvm-as.cpp +++ b/tools/llvm-as/llvm-as.cpp @@ -45,6 +45,10 @@ static cl::opt<bool> DisableOutput("disable-output", cl::desc("Disable output"), cl::init(false)); static cl::opt<bool> +EmitFunctionSummary("function-summary", cl::desc("Emit function summary index"), + cl::init(false)); + +static cl::opt<bool> DumpAsm("d", cl::desc("Print assembly as parsed"), cl::Hidden); static cl::opt<bool> @@ -77,7 +81,8 @@ static void WriteOutputFile(const Module *M) { } if (Force || !CheckBitcodeOutputToConsole(Out->os(), true)) - WriteBitcodeToFile(M, Out->os(), PreserveBitcodeUseListOrder); + WriteBitcodeToFile(M, Out->os(), PreserveBitcodeUseListOrder, + EmitFunctionSummary); // Declare success. Out->keep(); diff --git a/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp b/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp index 7672951f9c9b..fe68689def60 100644 --- a/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp +++ b/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp @@ -110,10 +110,16 @@ static const char *GetBlockName(unsigned BlockID, case bitc::TYPE_BLOCK_ID_NEW: return "TYPE_BLOCK_ID"; case bitc::CONSTANTS_BLOCK_ID: return "CONSTANTS_BLOCK"; case bitc::FUNCTION_BLOCK_ID: return "FUNCTION_BLOCK"; + case bitc::IDENTIFICATION_BLOCK_ID: + return "IDENTIFICATION_BLOCK_ID"; case bitc::VALUE_SYMTAB_BLOCK_ID: return "VALUE_SYMTAB"; case bitc::METADATA_BLOCK_ID: return "METADATA_BLOCK"; + case bitc::METADATA_KIND_BLOCK_ID: return "METADATA_KIND_BLOCK"; case bitc::METADATA_ATTACHMENT_ID: return "METADATA_ATTACHMENT_BLOCK"; case bitc::USELIST_BLOCK_ID: return "USELIST_BLOCK_ID"; + case bitc::FUNCTION_SUMMARY_BLOCK_ID: + return "FUNCTION_SUMMARY_BLOCK"; + case bitc::MODULE_STRTAB_BLOCK_ID: return "MODULE_STRTAB_BLOCK"; } } @@ -165,6 +171,15 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID, STRINGIFY_CODE(MODULE_CODE, ALIAS) STRINGIFY_CODE(MODULE_CODE, PURGEVALS) STRINGIFY_CODE(MODULE_CODE, GCNAME) + STRINGIFY_CODE(MODULE_CODE, VSTOFFSET) + STRINGIFY_CODE(MODULE_CODE, METADATA_VALUES) + } + case bitc::IDENTIFICATION_BLOCK_ID: + switch (CodeID) { + default: + return nullptr; + STRINGIFY_CODE(IDENTIFICATION_CODE, STRING) + STRINGIFY_CODE(IDENTIFICATION_CODE, EPOCH) } case bitc::PARAMATTR_BLOCK_ID: switch (CodeID) { @@ -241,6 +256,9 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID, STRINGIFY_CODE(FUNC_CODE, INST_SWITCH) STRINGIFY_CODE(FUNC_CODE, INST_INVOKE) STRINGIFY_CODE(FUNC_CODE, INST_UNREACHABLE) + STRINGIFY_CODE(FUNC_CODE, INST_CLEANUPRET) + STRINGIFY_CODE(FUNC_CODE, INST_CATCHRET) + STRINGIFY_CODE(FUNC_CODE, INST_CATCHPAD) STRINGIFY_CODE(FUNC_CODE, INST_PHI) STRINGIFY_CODE(FUNC_CODE, INST_ALLOCA) STRINGIFY_CODE(FUNC_CODE, INST_LOAD) @@ -260,6 +278,21 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID, default: return nullptr; STRINGIFY_CODE(VST_CODE, ENTRY) STRINGIFY_CODE(VST_CODE, BBENTRY) + STRINGIFY_CODE(VST_CODE, FNENTRY) + STRINGIFY_CODE(VST_CODE, COMBINED_FNENTRY) + } + case bitc::MODULE_STRTAB_BLOCK_ID: + switch (CodeID) { + default: + return nullptr; + STRINGIFY_CODE(MST_CODE, ENTRY) + } + case bitc::FUNCTION_SUMMARY_BLOCK_ID: + switch (CodeID) { + default: + return nullptr; + STRINGIFY_CODE(FS_CODE, PERMODULE_ENTRY) + STRINGIFY_CODE(FS_CODE, COMBINED_ENTRY) } case bitc::METADATA_ATTACHMENT_ID: switch(CodeID) { @@ -271,7 +304,7 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID, default:return nullptr; STRINGIFY_CODE(METADATA, STRING) STRINGIFY_CODE(METADATA, NAME) - STRINGIFY_CODE(METADATA, KIND) + STRINGIFY_CODE(METADATA, KIND) // Older bitcode has it in a MODULE_BLOCK STRINGIFY_CODE(METADATA, NODE) STRINGIFY_CODE(METADATA, VALUE) STRINGIFY_CODE(METADATA, OLD_NODE) @@ -301,6 +334,12 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID, STRINGIFY_CODE(METADATA, IMPORTED_ENTITY) STRINGIFY_CODE(METADATA, MODULE) } + case bitc::METADATA_KIND_BLOCK_ID: + switch (CodeID) { + default: + return nullptr; + STRINGIFY_CODE(METADATA, KIND) + } case bitc::USELIST_BLOCK_ID: switch(CodeID) { default:return nullptr; @@ -476,14 +515,38 @@ static bool ParseBlock(BitstreamCursor &Stream, unsigned BlockID, GetCodeName(Code, BlockID, *Stream.getBitStreamReader(), CurStreamType)) outs() << " codeid=" << Code; - if (Entry.ID != bitc::UNABBREV_RECORD) + const BitCodeAbbrev *Abbv = nullptr; + if (Entry.ID != bitc::UNABBREV_RECORD) { + Abbv = Stream.getAbbrev(Entry.ID); outs() << " abbrevid=" << Entry.ID; + } for (unsigned i = 0, e = Record.size(); i != e; ++i) outs() << " op" << i << "=" << (int64_t)Record[i]; outs() << "/>"; + if (Abbv) { + for (unsigned i = 1, e = Abbv->getNumOperandInfos(); i != e; ++i) { + const BitCodeAbbrevOp &Op = Abbv->getOperandInfo(i); + if (!Op.isEncoding() || Op.getEncoding() != BitCodeAbbrevOp::Array) + continue; + assert(i + 2 == e && "Array op not second to last"); + std::string Str; + bool ArrayIsPrintable = true; + for (unsigned j = i - 1, je = Record.size(); j != je; ++j) { + if (!isprint(static_cast<unsigned char>(Record[j]))) { + ArrayIsPrintable = false; + break; + } + Str += (char)Record[j]; + } + if (ArrayIsPrintable) + outs() << " record string = '" << Str << "'"; + break; + } + } + if (Blob.data()) { outs() << " blob data = "; if (ShowBinaryBlobs) { diff --git a/tools/llvm-c-test/llvm-c-test.h b/tools/llvm-c-test/llvm-c-test.h index 1b4976ae1423..7929fc4d19be 100644 --- a/tools/llvm-c-test/llvm-c-test.h +++ b/tools/llvm-c-test/llvm-c-test.h @@ -13,11 +13,13 @@ #ifndef LLVM_C_TEST_H #define LLVM_C_TEST_H +#include <stdbool.h> + // helpers.c void tokenize_stdin(void (*cb)(char **tokens, int ntokens)); // module.c -int module_dump(void); +int module_dump(bool Lazy, bool New); int module_list_functions(void); int module_list_globals(void); diff --git a/tools/llvm-c-test/main.c b/tools/llvm-c-test/main.c index 59cc749fb155..e6b6e17098b3 100644 --- a/tools/llvm-c-test/main.c +++ b/tools/llvm-c-test/main.c @@ -23,6 +23,14 @@ static void print_usage(void) { fprintf(stderr, " Commands:\n"); fprintf(stderr, " * --module-dump\n"); fprintf(stderr, " Read bytecode from stdin - print disassembly\n\n"); + fprintf(stderr, " * --lazy-module-dump\n"); + fprintf(stderr, + " Lazily read bytecode from stdin - print disassembly\n\n"); + fprintf(stderr, " * --new-module-dump\n"); + fprintf(stderr, " Read bytecode from stdin - print disassembly\n\n"); + fprintf(stderr, " * --lazy-new-module-dump\n"); + fprintf(stderr, + " Lazily read bytecode from stdin - print disassembly\n\n"); fprintf(stderr, " * --module-list-functions\n"); fprintf(stderr, " Read bytecode from stdin - list summary of functions\n\n"); @@ -49,8 +57,14 @@ int main(int argc, char **argv) { LLVMInitializeCore(pr); - if (argc == 2 && !strcmp(argv[1], "--module-dump")) { - return module_dump(); + if (argc == 2 && !strcmp(argv[1], "--lazy-new-module-dump")) { + return module_dump(true, true); + } else if (argc == 2 && !strcmp(argv[1], "--new-module-dump")) { + return module_dump(false, true); + } else if (argc == 2 && !strcmp(argv[1], "--lazy-module-dump")) { + return module_dump(true, false); + } else if (argc == 2 && !strcmp(argv[1], "--module-dump")) { + return module_dump(false, false); } else if (argc == 2 && !strcmp(argv[1], "--module-list-functions")) { return module_list_functions(); } else if (argc == 2 && !strcmp(argv[1], "--module-list-globals")) { diff --git a/tools/llvm-c-test/module.c b/tools/llvm-c-test/module.c index 2661fc81d881..a6c47bf5fa16 100644 --- a/tools/llvm-c-test/module.c +++ b/tools/llvm-c-test/module.c @@ -19,7 +19,14 @@ #include <stdlib.h> #include <string.h> -static LLVMModuleRef load_module(void) { +static void diagnosticHandler(LLVMDiagnosticInfoRef DI, void *C) { + char *CErr = LLVMGetDiagInfoDescription(DI); + fprintf(stderr, "Error with new bitcode parser: %s\n", CErr); + LLVMDisposeMessage(CErr); + exit(1); +} + +static LLVMModuleRef load_module(bool Lazy, bool New) { LLVMMemoryBufferRef MB; LLVMModuleRef M; char *msg = NULL; @@ -29,18 +36,35 @@ static LLVMModuleRef load_module(void) { exit(1); } - if (LLVMParseBitcode(MB, &M, &msg)) { + LLVMBool Ret; + if (New) { + LLVMContextRef C = LLVMGetGlobalContext(); + LLVMContextSetDiagnosticHandler(C, diagnosticHandler, NULL); + if (Lazy) + Ret = LLVMGetBitcodeModule2(MB, &M); + else + Ret = LLVMParseBitcode2(MB, &M); + } else { + if (Lazy) + Ret = LLVMGetBitcodeModule(MB, &M, &msg); + else + Ret = LLVMParseBitcode(MB, &M, &msg); + } + + if (Ret) { fprintf(stderr, "Error parsing bitcode: %s\n", msg); LLVMDisposeMemoryBuffer(MB); exit(1); } - LLVMDisposeMemoryBuffer(MB); + if (!Lazy) + LLVMDisposeMemoryBuffer(MB); + return M; } -int module_dump(void) { - LLVMModuleRef M = load_module(); +int module_dump(bool Lazy, bool New) { + LLVMModuleRef M = load_module(Lazy, New); char *irstr = LLVMPrintModuleToString(M); puts(irstr); @@ -52,7 +76,7 @@ int module_dump(void) { } int module_list_functions(void) { - LLVMModuleRef M = load_module(); + LLVMModuleRef M = load_module(false, false); LLVMValueRef f; f = LLVMGetFirstFunction(M); @@ -93,7 +117,7 @@ int module_list_functions(void) { } int module_list_globals(void) { - LLVMModuleRef M = load_module(); + LLVMModuleRef M = load_module(false, false); LLVMValueRef g; g = LLVMGetFirstGlobal(M); diff --git a/tools/llvm-c-test/object.c b/tools/llvm-c-test/object.c index a5421d9066d5..43521787b609 100644 --- a/tools/llvm-c-test/object.c +++ b/tools/llvm-c-test/object.c @@ -13,6 +13,7 @@ \*===----------------------------------------------------------------------===*/ #include "llvm-c-test.h" +#include "llvm-c/Core.h" #include "llvm-c/Object.h" #include <stdio.h> #include <stdlib.h> diff --git a/tools/llvm-config/BuildVariables.inc.in b/tools/llvm-config/BuildVariables.inc.in index 3f51f491a7a4..345f47e91b4a 100644 --- a/tools/llvm-config/BuildVariables.inc.in +++ b/tools/llvm-config/BuildVariables.inc.in @@ -26,3 +26,8 @@ #define LLVM_LIBDIR_SUFFIX "@LLVM_LIBDIR_SUFFIX@" #define LLVM_TARGETS_BUILT "@LLVM_TARGETS_BUILT@" #define LLVM_SYSTEM_LIBS "@LLVM_SYSTEM_LIBS@" +#define LLVM_BUILD_SYSTEM "@LLVM_BUILD_SYSTEM@" +#define LLVM_HAS_RTTI "@LLVM_HAS_RTTI@" +#define LLVM_ENABLE_DYLIB "@LLVM_BUILD_LLVM_DYLIB@" +#define LLVM_ENABLE_SHARED "@LLVM_ENABLE_SHARED@" +#define LLVM_DYLIB_COMPONENTS "@LLVM_DYLIB_COMPONENTS@" diff --git a/tools/llvm-config/CMakeLists.txt b/tools/llvm-config/CMakeLists.txt index edbd8c950d7b..83794bb3fddd 100644 --- a/tools/llvm-config/CMakeLists.txt +++ b/tools/llvm-config/CMakeLists.txt @@ -24,10 +24,18 @@ set(LLVM_OBJ_ROOT ${LLVM_BINARY_DIR}) set(LLVM_CPPFLAGS "${CMAKE_CPP_FLAGS} ${CMAKE_CPP_FLAGS_${uppercase_CMAKE_BUILD_TYPE}} ${LLVM_DEFINITIONS}") set(LLVM_CFLAGS "${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${uppercase_CMAKE_BUILD_TYPE}} ${LLVM_DEFINITIONS}") set(LLVM_CXXFLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${uppercase_CMAKE_BUILD_TYPE}} ${COMPILE_FLAGS} ${LLVM_DEFINITIONS}") +set(LLVM_BUILD_SYSTEM cmake) +set(LLVM_HAS_RTTI ${LLVM_CONFIG_HAS_RTTI}) + # Use the C++ link flags, since they should be a superset of C link flags. set(LLVM_LDFLAGS "${CMAKE_CXX_LINK_FLAGS}") set(LLVM_BUILDMODE ${CMAKE_BUILD_TYPE}) set(LLVM_SYSTEM_LIBS ${SYSTEM_LIBS}) +if(BUILD_SHARED_LIBS) + set(LLVM_ENABLE_SHARED ON) +else() + set(LLVM_ENABLE_SHARED OFF) +endif() string(REPLACE ";" " " LLVM_TARGETS_BUILT "${LLVM_TARGETS_TO_BUILD}") configure_file(${BUILDVARIABLES_SRCPATH} ${BUILDVARIABLES_OBJPATH} @ONLY) diff --git a/tools/llvm-config/Makefile b/tools/llvm-config/Makefile index 1ff8b6f04063..d2fe2cfa34aa 100644 --- a/tools/llvm-config/Makefile +++ b/tools/llvm-config/Makefile @@ -38,6 +38,12 @@ else LLVM_SYSTEM_LIBS := $(LIBS) endif +ifneq ($(REQUIRES_RTTI), 1) + LLVM_HAS_RTTI := NO +else + LLVM_HAS_RTTI := YES +endif + # This is blank for now. We need to be careful about adding stuff here: # LDFLAGS tend not to be portable, and we don't currently require the # user to use libtool when linking against LLVM. @@ -65,6 +71,18 @@ $(ObjDir)/BuildVariables.inc: $(BUILDVARIABLES_SRCPATH) Makefile $(ObjDir)/.dir >> temp.sed $(Verb) $(ECHO) 's/@LLVM_TARGETS_BUILT@/$(subst /,\/,$(TARGETS_TO_BUILD))/' \ >> temp.sed + $(if $(filter-out $(ENABLE_SHARED),0),\ + $(Verb) $(ECHO) 's/@LLVM_BUILD_LLVM_DYLIB@/ON/',\ + $(Verb) $(ECHO) 's/@LLVM_BUILD_LLVM_DYLIB@/OFF/') \ + >> temp.sed + $(Verb) $(ECHO) 's/@LLVM_ENABLE_SHARED@/OFF/' \ + >> temp.sed + $(Verb) $(ECHO) 's/@LLVM_DYLIB_COMPONENTS@/all/' \ + >> temp.sed + $(Verb) $(ECHO) 's/@LLVM_BUILD_SYSTEM@/autoconf/' \ + >> temp.sed + $(Verb) $(ECHO) 's/@LLVM_HAS_RTTI@/$(LLVM_HAS_RTTI)/' \ + >> temp.sed $(Verb) $(SED) -f temp.sed < $< > $@ $(Verb) $(RM) temp.sed diff --git a/tools/llvm-config/llvm-config.cpp b/tools/llvm-config/llvm-config.cpp index 879b9ab0945b..80f627936d0c 100644 --- a/tools/llvm-config/llvm-config.cpp +++ b/tools/llvm-config/llvm-config.cpp @@ -30,6 +30,7 @@ #include <cstdlib> #include <set> #include <vector> +#include <unordered_set> using namespace llvm; @@ -51,12 +52,16 @@ using namespace llvm; /// \param Name - The component to traverse. /// \param ComponentMap - A prebuilt map of component names to descriptors. /// \param VisitedComponents [in] [out] - The set of already visited components. -/// \param RequiredLibs [out] - The ordered list of required libraries. -static void VisitComponent(StringRef Name, +/// \param RequiredLibs [out] - The ordered list of required +/// libraries. +/// \param GetComponentNames - Get the component names instead of the +/// library name. +static void VisitComponent(const std::string& Name, const StringMap<AvailableComponent*> &ComponentMap, std::set<AvailableComponent*> &VisitedComponents, - std::vector<StringRef> &RequiredLibs, - bool IncludeNonInstalled) { + std::vector<std::string> &RequiredLibs, + bool IncludeNonInstalled, bool GetComponentNames, + const std::string *ActiveLibDir, bool *HasMissing) { // Lookup the component. AvailableComponent *AC = ComponentMap.lookup(Name); assert(AC && "Invalid component name!"); @@ -74,12 +79,22 @@ static void VisitComponent(StringRef Name, // Otherwise, visit all the dependencies. for (unsigned i = 0; AC->RequiredLibraries[i]; ++i) { VisitComponent(AC->RequiredLibraries[i], ComponentMap, VisitedComponents, - RequiredLibs, IncludeNonInstalled); + RequiredLibs, IncludeNonInstalled, GetComponentNames, + ActiveLibDir, HasMissing); + } + + if (GetComponentNames) { + RequiredLibs.push_back(Name); + return; } // Add to the required library list. - if (AC->Library) + if (AC->Library) { + if (!IncludeNonInstalled && HasMissing && !*HasMissing && ActiveLibDir) { + *HasMissing = !sys::fs::exists(*ActiveLibDir + "/" + AC->Library); + } RequiredLibs.push_back(AC->Library); + } } /// \brief Compute the list of required libraries for a given list of @@ -87,14 +102,15 @@ static void VisitComponent(StringRef Name, /// appear prior to their dependencies). /// /// \param Components - The names of the components to find libraries for. -/// \param RequiredLibs [out] - On return, the ordered list of libraries that -/// are required to link the given components. /// \param IncludeNonInstalled - Whether non-installed components should be /// reported. -static void ComputeLibsForComponents(const std::vector<StringRef> &Components, - std::vector<StringRef> &RequiredLibs, - bool IncludeNonInstalled) { - std::set<AvailableComponent*> VisitedComponents; +/// \param GetComponentNames - True if one would prefer the component names. +static std::vector<std::string> +ComputeLibsForComponents(const std::vector<StringRef> &Components, + bool IncludeNonInstalled, bool GetComponentNames, + const std::string *ActiveLibDir, bool *HasMissing) { + std::vector<std::string> RequiredLibs; + std::set<AvailableComponent *> VisitedComponents; // Build a map of component names to information. StringMap<AvailableComponent*> ComponentMap; @@ -116,12 +132,15 @@ static void ComputeLibsForComponents(const std::vector<StringRef> &Components, } VisitComponent(ComponentLower, ComponentMap, VisitedComponents, - RequiredLibs, IncludeNonInstalled); + RequiredLibs, IncludeNonInstalled, GetComponentNames, + ActiveLibDir, HasMissing); } // The list is now ordered with leafs first, we want the libraries to printed // in the reverse order of dependency. std::reverse(RequiredLibs.begin(), RequiredLibs.end()); + + return RequiredLibs; } /* *** */ @@ -157,6 +176,9 @@ Options:\n\ --host-target Target triple used to configure LLVM.\n\ --build-mode Print build mode of LLVM tree (e.g. Debug or Release).\n\ --assertion-mode Print assertion mode of LLVM tree (ON or OFF).\n\ + --build-system Print the build system used to build LLVM (autoconf or cmake).\n\ + --has-rtti Print whether or not LLVM was built with rtti (YES or NO).\n\ + --shared-mode Print how the provided components can be collectively linked (`shared` or `static`).\n\ Typical components:\n\ all All LLVM libraries (default).\n\ engine Either a native JIT or a bitcode interpreter.\n"; @@ -171,10 +193,34 @@ std::string GetExecutablePath(const char *Argv0) { return llvm::sys::fs::getMainExecutable(Argv0, P); } +/// \brief Expand the semi-colon delimited LLVM_DYLIB_COMPONENTS into +/// the full list of components. +std::vector<std::string> GetAllDyLibComponents(const bool IsInDevelopmentTree, + const bool GetComponentNames) { + std::vector<StringRef> DyLibComponents; + + StringRef DyLibComponentsStr(LLVM_DYLIB_COMPONENTS); + size_t Offset = 0; + while (true) { + const size_t NextOffset = DyLibComponentsStr.find(';', Offset); + DyLibComponents.push_back(DyLibComponentsStr.substr(Offset, NextOffset)); + if (NextOffset == std::string::npos) { + break; + } + Offset = NextOffset + 1; + } + + assert(!DyLibComponents.empty()); + + return ComputeLibsForComponents(DyLibComponents, + /*IncludeNonInstalled=*/IsInDevelopmentTree, + GetComponentNames, nullptr, nullptr); +} + int main(int argc, char **argv) { std::vector<StringRef> Components; bool PrintLibs = false, PrintLibNames = false, PrintLibFiles = false; - bool PrintSystemLibs = false; + bool PrintSystemLibs = false, PrintSharedMode = false; bool HasAnyOption = false; // llvm-config is designed to support being run both from a development tree @@ -269,6 +315,108 @@ int main(int argc, char **argv) { ActiveIncludeOption = "-I" + ActiveIncludeDir; } + /// We only use `shared library` mode in cases where the static library form + /// of the components provided are not available; note however that this is + /// skipped if we're run from within the build dir. However, once installed, + /// we still need to provide correct output when the static archives are + /// removed or, as in the case of CMake's `BUILD_SHARED_LIBS`, never present + /// in the first place. This can't be done at configure/build time. + + StringRef SharedExt, SharedVersionedExt, SharedDir, SharedPrefix, StaticExt, + StaticPrefix, StaticDir = "lib"; + const Triple HostTriple(Triple::normalize(LLVM_DEFAULT_TARGET_TRIPLE)); + if (HostTriple.isOSWindows()) { + SharedExt = "dll"; + SharedVersionedExt = PACKAGE_VERSION ".dll"; + StaticExt = "a"; + SharedDir = ActiveBinDir; + StaticDir = ActiveLibDir; + StaticPrefix = SharedPrefix = "lib"; + } else if (HostTriple.isOSDarwin()) { + SharedExt = "dylib"; + SharedVersionedExt = PACKAGE_VERSION ".dylib"; + StaticExt = "a"; + StaticDir = SharedDir = ActiveLibDir; + StaticPrefix = SharedPrefix = "lib"; + } else { + // default to the unix values: + SharedExt = "so"; + SharedVersionedExt = PACKAGE_VERSION ".so"; + StaticExt = "a"; + StaticDir = SharedDir = ActiveLibDir; + StaticPrefix = SharedPrefix = "lib"; + } + + const bool BuiltDyLib = (std::strcmp(LLVM_ENABLE_DYLIB, "ON") == 0); + + enum { CMake, AutoConf } ConfigTool; + if (std::strcmp(LLVM_BUILD_SYSTEM, "cmake") == 0) { + ConfigTool = CMake; + } else { + ConfigTool = AutoConf; + } + + /// CMake style shared libs, ie each component is in a shared library. + const bool BuiltSharedLibs = + (ConfigTool == CMake && std::strcmp(LLVM_ENABLE_SHARED, "ON") == 0); + + bool DyLibExists = false; + const std::string DyLibName = + (SharedPrefix + "LLVM-" + SharedVersionedExt).str(); + + if (BuiltDyLib) { + DyLibExists = sys::fs::exists(SharedDir + "/" + DyLibName); + } + + /// Get the component's library name without the lib prefix and the + /// extension. Returns true if Lib is in a recognized format. + auto GetComponentLibraryNameSlice = [&](const StringRef &Lib, + StringRef &Out) { + if (Lib.startswith("lib")) { + unsigned FromEnd; + if (Lib.endswith(StaticExt)) { + FromEnd = StaticExt.size() + 1; + } else if (Lib.endswith(SharedExt)) { + FromEnd = SharedExt.size() + 1; + } else { + FromEnd = 0; + } + + if (FromEnd != 0) { + Out = Lib.slice(3, Lib.size() - FromEnd); + return true; + } + } + + return false; + }; + /// Maps Unixizms to the host platform. + auto GetComponentLibraryFileName = [&](const StringRef &Lib, + const bool ForceShared) { + std::string LibFileName = Lib; + StringRef LibName; + if (GetComponentLibraryNameSlice(Lib, LibName)) { + if (BuiltSharedLibs || ForceShared) { + LibFileName = (SharedPrefix + LibName + "." + SharedExt).str(); + } else { + // default to static + LibFileName = (StaticPrefix + LibName + "." + StaticExt).str(); + } + } + + return LibFileName; + }; + /// Get the full path for a possibly shared component library. + auto GetComponentLibraryPath = [&](const StringRef &Name, + const bool ForceShared) { + auto LibFileName = GetComponentLibraryFileName(Name, ForceShared); + if (BuiltSharedLibs || ForceShared) { + return (SharedDir + "/" + LibFileName).str(); + } else { + return (StaticDir + "/" + LibFileName).str(); + } + }; + raw_ostream &OS = outs(); for (int i = 1; i != argc; ++i) { StringRef Arg = argv[i]; @@ -302,13 +450,33 @@ int main(int argc, char **argv) { } else if (Arg == "--libfiles") { PrintLibFiles = true; } else if (Arg == "--components") { + /// If there are missing static archives and a dylib was + /// built, print LLVM_DYLIB_COMPONENTS instead of everything + /// in the manifest. + std::vector<std::string> Components; for (unsigned j = 0; j != array_lengthof(AvailableComponents); ++j) { // Only include non-installed components when in a development tree. if (!AvailableComponents[j].IsInstalled && !IsInDevelopmentTree) continue; - OS << ' '; - OS << AvailableComponents[j].Name; + Components.push_back(AvailableComponents[j].Name); + if (AvailableComponents[j].Library && !IsInDevelopmentTree) { + if (DyLibExists && + !sys::fs::exists(GetComponentLibraryPath( + AvailableComponents[j].Library, false))) { + Components = GetAllDyLibComponents(IsInDevelopmentTree, true); + std::sort(Components.begin(), Components.end()); + break; + } + } + } + + for (unsigned I = 0; I < Components.size(); ++I) { + if (I) { + OS << ' '; + } + + OS << Components[I]; } OS << '\n'; } else if (Arg == "--targets-built") { @@ -323,6 +491,12 @@ int main(int argc, char **argv) { #else OS << "ON\n"; #endif + } else if (Arg == "--build-system") { + OS << LLVM_BUILD_SYSTEM << '\n'; + } else if (Arg == "--has-rtti") { + OS << LLVM_HAS_RTTI << '\n'; + } else if (Arg == "--shared-mode") { + PrintSharedMode = true; } else if (Arg == "--obj-root") { OS << ActivePrefix << '\n'; } else if (Arg == "--src-root") { @@ -338,35 +512,84 @@ int main(int argc, char **argv) { if (!HasAnyOption) usage(); - if (PrintLibs || PrintLibNames || PrintLibFiles || PrintSystemLibs) { + if (PrintLibs || PrintLibNames || PrintLibFiles || PrintSystemLibs || + PrintSharedMode) { + + if (PrintSharedMode && BuiltSharedLibs) { + OS << "shared\n"; + return 0; + } + // If no components were specified, default to "all". if (Components.empty()) Components.push_back("all"); // Construct the list of all the required libraries. - std::vector<StringRef> RequiredLibs; - ComputeLibsForComponents(Components, RequiredLibs, - /*IncludeNonInstalled=*/IsInDevelopmentTree); + bool HasMissing = false; + std::vector<std::string> RequiredLibs = + ComputeLibsForComponents(Components, + /*IncludeNonInstalled=*/IsInDevelopmentTree, + false, &ActiveLibDir, &HasMissing); + + if (PrintSharedMode) { + std::unordered_set<std::string> FullDyLibComponents; + std::vector<std::string> DyLibComponents = + GetAllDyLibComponents(IsInDevelopmentTree, false); + + for (auto &Component : DyLibComponents) { + FullDyLibComponents.insert(Component); + } + DyLibComponents.clear(); + + for (auto &Lib : RequiredLibs) { + if (!FullDyLibComponents.count(Lib)) { + OS << "static\n"; + return 0; + } + } + FullDyLibComponents.clear(); + + if (HasMissing && DyLibExists) { + OS << "shared\n"; + return 0; + } else { + OS << "static\n"; + return 0; + } + } if (PrintLibs || PrintLibNames || PrintLibFiles) { - for (unsigned i = 0, e = RequiredLibs.size(); i != e; ++i) { - StringRef Lib = RequiredLibs[i]; - if (i) - OS << ' '; + auto PrintForLib = [&](const StringRef &Lib, const bool ForceShared) { if (PrintLibNames) { - OS << Lib; + OS << GetComponentLibraryFileName(Lib, ForceShared); } else if (PrintLibFiles) { - OS << ActiveLibDir << '/' << Lib; + OS << GetComponentLibraryPath(Lib, ForceShared); } else if (PrintLibs) { // If this is a typical library name, include it using -l. - if (Lib.startswith("lib") && Lib.endswith(".a")) { - OS << "-l" << Lib.slice(3, Lib.size()-2); - continue; + StringRef LibName; + if (Lib.startswith("lib")) { + if (GetComponentLibraryNameSlice(Lib, LibName)) { + OS << "-l" << LibName; + } else { + OS << "-l:" << GetComponentLibraryFileName(Lib, ForceShared); + } + } else { + // Otherwise, print the full path. + OS << GetComponentLibraryPath(Lib, ForceShared); } + } + }; + + if (HasMissing && DyLibExists) { + PrintForLib(DyLibName, true); + } else { + for (unsigned i = 0, e = RequiredLibs.size(); i != e; ++i) { + auto Lib = RequiredLibs[i]; + if (i) + OS << ' '; - // Otherwise, print the full path. - OS << ActiveLibDir << '/' << Lib; + PrintForLib(Lib, false); } } OS << '\n'; diff --git a/tools/llvm-cov/CoverageReport.cpp b/tools/llvm-cov/CoverageReport.cpp index 497c2f88f268..ed01a2e154f1 100644 --- a/tools/llvm-cov/CoverageReport.cpp +++ b/tools/llvm-cov/CoverageReport.cpp @@ -20,7 +20,7 @@ using namespace llvm; namespace { /// \brief Helper struct which prints trimmed and aligned columns. struct Column { - enum TrimKind { NoTrim, LeftTrim, RightTrim }; + enum TrimKind { NoTrim, WidthTrim, LeftTrim, RightTrim }; enum AlignmentKind { LeftAlignment, RightAlignment }; @@ -30,7 +30,7 @@ struct Column { AlignmentKind Alignment; Column(StringRef Str, unsigned Width) - : Str(Str), Width(Width), Trim(NoTrim), Alignment(LeftAlignment) {} + : Str(Str), Width(Width), Trim(WidthTrim), Alignment(LeftAlignment) {} Column &set(TrimKind Value) { Trim = Value; @@ -44,6 +44,7 @@ struct Column { void render(raw_ostream &OS) const; }; + raw_ostream &operator<<(raw_ostream &OS, const Column &Value) { Value.render(OS); return OS; @@ -64,6 +65,9 @@ void Column::render(raw_ostream &OS) const { switch (Trim) { case NoTrim: + OS << Str; + break; + case WidthTrim: OS << Str.substr(0, Width); break; case LeftTrim: @@ -84,8 +88,19 @@ 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}; +static size_t FileReportColumns[] = {25, 10, 8, 8, 10, 10}; +static size_t FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8}; + +/// \brief Adjust column widths to fit long file paths and function names. +static void adjustColumnWidths(coverage::CoverageMapping *CM) { + for (StringRef Filename : CM->getUniqueSourceFiles()) { + FileReportColumns[0] = std::max(FileReportColumns[0], Filename.size()); + for (const auto &F : CM->getCoveredFunctions(Filename)) { + FunctionReportColumns[0] = + std::max(FunctionReportColumns[0], F.Name.size()); + } + } +} /// \brief Prints a horizontal divider which spans across the given columns. template <typename T, size_t N> @@ -108,8 +123,9 @@ static raw_ostream::Colors determineCoveragePercentageColor(const T &Info) { } 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); + OS << column(File.Name, FileReportColumns[0], Column::NoTrim) + << format("%*u", FileReportColumns[1], + (unsigned)File.RegionCoverage.NumRegions); Options.colored_ostream(OS, File.RegionCoverage.isFullyCovered() ? raw_ostream::GREEN : raw_ostream::RED) @@ -157,6 +173,7 @@ void CoverageReport::render(const FunctionCoverageSummary &Function, void CoverageReport::renderFunctionReports(ArrayRef<std::string> Files, raw_ostream &OS) { + adjustColumnWidths(Coverage.get()); bool isFirst = true; for (StringRef Filename : Files) { if (isFirst) @@ -191,6 +208,7 @@ void CoverageReport::renderFunctionReports(ArrayRef<std::string> Files, } void CoverageReport::renderFileReports(raw_ostream &OS) { + adjustColumnWidths(Coverage.get()); OS << column("Filename", FileReportColumns[0]) << column("Regions", FileReportColumns[1], Column::RightAlignment) << column("Miss", FileReportColumns[2], Column::RightAlignment) @@ -200,6 +218,7 @@ void CoverageReport::renderFileReports(raw_ostream &OS) { << "\n"; renderDivider(FileReportColumns, OS); OS << "\n"; + FileCoverageSummary Totals("TOTAL"); for (StringRef Filename : Coverage->getUniqueSourceFiles()) { FileCoverageSummary Summary(Filename); diff --git a/tools/llvm-cov/CoverageViewOptions.h b/tools/llvm-cov/CoverageViewOptions.h index 94b55fe793fc..1208fad79176 100644 --- a/tools/llvm-cov/CoverageViewOptions.h +++ b/tools/llvm-cov/CoverageViewOptions.h @@ -24,6 +24,7 @@ struct CoverageViewOptions { bool ShowLineStatsOrRegionMarkers; bool ShowExpandedRegions; bool ShowFunctionInstantiations; + bool ShowFullFilenames; /// \brief Change the output's stream color if the colors are enabled. ColoredRawOstream colored_ostream(raw_ostream &OS, diff --git a/tools/llvm-cov/gcov.cpp b/tools/llvm-cov/gcov.cpp index 4377a50b5566..a5343fa29afc 100644 --- a/tools/llvm-cov/gcov.cpp +++ b/tools/llvm-cov/gcov.cpp @@ -26,7 +26,7 @@ using namespace llvm; static void reportCoverage(StringRef SourceFile, StringRef ObjectDir, const std::string &InputGCNO, const std::string &InputGCDA, bool DumpGCOV, - const GCOVOptions &Options) { + const GCOV::Options &Options) { SmallString<128> CoverageFileStem(ObjectDir); if (CoverageFileStem.empty()) { // If no directory was specified with -o, look next to the source file. @@ -143,8 +143,8 @@ int gcovMain(int argc, const char *argv[]) { cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); - GCOVOptions Options(AllBlocks, BranchProb, BranchCount, FuncSummary, - PreservePaths, UncondBranch, LongNames, NoOutput); + GCOV::Options Options(AllBlocks, BranchProb, BranchCount, FuncSummary, + PreservePaths, UncondBranch, LongNames, NoOutput); for (const auto &SourceFile : SourceFiles) reportCoverage(SourceFile, ObjectDir, InputGCNO, InputGCDA, DumpGCOV, diff --git a/tools/llvm-cxxdump/llvm-cxxdump.cpp b/tools/llvm-cxxdump/llvm-cxxdump.cpp index 4e06be9e78b9..3dda69266a2d 100644 --- a/tools/llvm-cxxdump/llvm-cxxdump.cpp +++ b/tools/llvm-cxxdump/llvm-cxxdump.cpp @@ -40,18 +40,14 @@ cl::list<std::string> InputFilenames(cl::Positional, cl::ZeroOrMore); } // namespace opts -static int ReturnValue = EXIT_SUCCESS; - namespace llvm { -static bool error(std::error_code EC) { +static void error(std::error_code EC) { if (!EC) - return false; - - ReturnValue = EXIT_FAILURE; + return; outs() << "\nError reading file: " << EC.message() << ".\n"; outs().flush(); - return true; + exit(1); } } // namespace llvm @@ -59,38 +55,24 @@ static bool error(std::error_code EC) { static void reportError(StringRef Input, StringRef Message) { if (Input == "-") Input = "<stdin>"; - errs() << Input << ": " << Message << "\n"; errs().flush(); - ReturnValue = EXIT_FAILURE; + exit(1); } 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 std::map<SectionRef, SmallVector<SectionRef, 1>> SectionRelocMap; -static bool collectRelocatedSymbols(const ObjectFile *Obj, +static void 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 SectionRef &SR : SectionRelocMap[Sec]) { for (const object::RelocationRef &Reloc : SR.relocations()) { if (I == E) break; @@ -98,8 +80,7 @@ static bool collectRelocatedSymbols(const ObjectFile *Obj, if (RelocSymI == Obj->symbol_end()) continue; ErrorOr<StringRef> RelocSymName = RelocSymI->getName(); - if (error(RelocSymName.getError())) - return true; + error(RelocSymName.getError()); uint64_t Offset = Reloc.getOffset(); if (Offset >= SymOffset && Offset < SymEnd) { *I = *RelocSymName; @@ -107,29 +88,26 @@ static bool collectRelocatedSymbols(const ObjectFile *Obj, } } } - return false; } -static bool collectRelocationOffsets( +static void 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 SectionRef &SR : SectionRelocMap[Sec]) { for (const object::RelocationRef &Reloc : SR.relocations()) { const object::symbol_iterator RelocSymI = Reloc.getSymbol(); if (RelocSymI == Obj->symbol_end()) continue; ErrorOr<StringRef> RelocSymName = RelocSymI->getName(); - if (error(RelocSymName.getError())) - return true; + error(RelocSymName.getError()); uint64_t Offset = Reloc.getOffset(); if (Offset >= SymOffset && Offset < SymEnd) Collection[std::make_pair(SymName, Offset - SymOffset)] = *RelocSymName; } } - return false; } static void dumpCXXData(const ObjectFile *Obj) { @@ -182,6 +160,13 @@ static void dumpCXXData(const ObjectFile *Obj) { std::map<std::pair<StringRef, uint64_t>, StringRef> VTTEntries; std::map<StringRef, StringRef> TINames; + SectionRelocMap.clear(); + for (const SectionRef &Section : Obj->sections()) { + section_iterator Sec2 = Section.getRelocatedSection(); + if (Sec2 != Obj->section_end()) + SectionRelocMap[*Sec2].push_back(Section); + } + uint8_t BytesInAddress = Obj->getBytesInAddress(); std::vector<std::pair<SymbolRef, uint64_t>> SymAddr = @@ -191,12 +176,11 @@ static void dumpCXXData(const ObjectFile *Obj) { object::SymbolRef Sym = P.first; uint64_t SymSize = P.second; ErrorOr<StringRef> SymNameOrErr = Sym.getName(); - if (error(SymNameOrErr.getError())) - return; + error(SymNameOrErr.getError()); StringRef SymName = *SymNameOrErr; - object::section_iterator SecI(Obj->section_begin()); - if (error(Sym.getSection(SecI))) - return; + ErrorOr<object::section_iterator> SecIOrErr = Sym.getSection(); + error(SecIOrErr.getError()); + object::section_iterator SecI = *SecIOrErr; // Skip external symbols. if (SecI == Obj->section_end()) continue; @@ -205,11 +189,9 @@ static void dumpCXXData(const ObjectFile *Obj) { if (Sec.isBSS() || Sec.isVirtual()) continue; StringRef SecContents; - if (error(Sec.getContents(SecContents))) - return; + error(Sec.getContents(SecContents)); ErrorOr<uint64_t> SymAddressOrErr = Sym.getAddress(); - if (error(SymAddressOrErr.getError())) - return; + error(SymAddressOrErr.getError()); uint64_t SymAddress = *SymAddressOrErr; uint64_t SecAddress = Sec.getAddress(); uint64_t SecSize = Sec.getSize(); @@ -236,23 +218,19 @@ static void dumpCXXData(const ObjectFile *Obj) { // Complete object locators in the MS-ABI start with '??_R4' else if (SymName.startswith("??_R4")) { CompleteObjectLocator COL; - COL.Data = ArrayRef<little32_t>( + COL.Data = makeArrayRef( 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; + collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I, E); 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>( + CHD.Data = makeArrayRef( 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; + collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I, E); CHDs[SymName] = CHD; } // Class hierarchy descriptors in the MS-ABI start with '??_R2' @@ -265,12 +243,10 @@ static void dumpCXXData(const ObjectFile *Obj) { // Base class descriptors in the MS-ABI start with '??_R1' else if (SymName.startswith("??_R1")) { BaseClassDescriptor BCD; - BCD.Data = ArrayRef<little32_t>( + BCD.Data = makeArrayRef( 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; + collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I, E); BCDs[SymName] = BCD; } // Type descriptors in the MS-ABI start with '??_R0' @@ -283,9 +259,7 @@ static void dumpCXXData(const ObjectFile *Obj) { 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; + collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I, E); TDs[SymName] = TD; } // Throw descriptors in the MS-ABI start with '_TI' @@ -316,9 +290,7 @@ static void dumpCXXData(const ObjectFile *Obj) { CT.VirtualBaseAdjustmentOffset = DataPtr[4]; CT.Size = DataPtr[5]; StringRef *I = std::begin(CT.Symbols), *E = std::end(CT.Symbols); - if (collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I, - E)) - return; + collectRelocatedSymbols(Obj, Sec, SecAddress, SymAddress, SymSize, I, E); CTs[SymName] = CT; } // Construction vtables in the Itanium ABI start with '_ZTT' or '__ZTT'. @@ -510,7 +482,9 @@ static void dumpCXXData(const ObjectFile *Obj) { } static void dumpArchive(const Archive *Arc) { - for (const Archive::Child &ArcC : Arc->children()) { + for (auto &ErrorOrChild : Arc->children()) { + error(ErrorOrChild.getError()); + const Archive::Child &ArcC = *ErrorOrChild; ErrorOr<std::unique_ptr<Binary>> ChildOrErr = ArcC.getAsBinary(); if (std::error_code EC = ChildOrErr.getError()) { // Ignore non-object files. @@ -527,12 +501,6 @@ static void dumpArchive(const Archive *Arc) { } static void dumpInput(StringRef File) { - // If file isn't stdin, check that it exists. - if (File != "-" && !sys::fs::exists(File)) { - reportError(File, cxxdump_error::file_not_found); - return; - } - // Attempt to open the binary. ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(File); if (std::error_code EC = BinaryOrErr.getError()) { @@ -569,5 +537,5 @@ int main(int argc, const char *argv[]) { std::for_each(opts::InputFilenames.begin(), opts::InputFilenames.end(), dumpInput); - return ReturnValue; + return EXIT_SUCCESS; } diff --git a/tools/llvm-diff/DiffLog.cpp b/tools/llvm-diff/DiffLog.cpp index 24a1b08d545d..ed86058f1af1 100644 --- a/tools/llvm-diff/DiffLog.cpp +++ b/tools/llvm-diff/DiffLog.cpp @@ -20,7 +20,8 @@ using namespace llvm; LogBuilder::~LogBuilder() { - consumer.logf(*this); + if (consumer) + consumer->logf(*this); } StringRef LogBuilder::getFormat() const { return Format; } diff --git a/tools/llvm-diff/DiffLog.h b/tools/llvm-diff/DiffLog.h index 8eb53ffffccf..8f28461afdde 100644 --- a/tools/llvm-diff/DiffLog.h +++ b/tools/llvm-diff/DiffLog.h @@ -27,7 +27,7 @@ namespace llvm { /// A temporary-object class for building up log messages. class LogBuilder { - Consumer &consumer; + Consumer *consumer; /// The use of a stored StringRef here is okay because /// LogBuilder should be used only as a temporary, and as a @@ -38,8 +38,12 @@ namespace llvm { SmallVector<Value*, 4> Arguments; public: - LogBuilder(Consumer &c, StringRef Format) - : consumer(c), Format(Format) {} + LogBuilder(Consumer &c, StringRef Format) : consumer(&c), Format(Format) {} + LogBuilder(LogBuilder &&L) + : consumer(L.consumer), Format(L.Format), + Arguments(std::move(L.Arguments)) { + L.consumer = nullptr; + } LogBuilder &operator<<(Value *V) { Arguments.push_back(V); diff --git a/tools/llvm-diff/DifferenceEngine.cpp b/tools/llvm-diff/DifferenceEngine.cpp index 7d379ef5dc41..456560b093ab 100644 --- a/tools/llvm-diff/DifferenceEngine.cpp +++ b/tools/llvm-diff/DifferenceEngine.cpp @@ -599,7 +599,7 @@ void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart, TerminatorInst *RTerm = RStart->getParent()->getTerminator(); if (isa<BranchInst>(LTerm) && isa<InvokeInst>(RTerm)) { if (cast<BranchInst>(LTerm)->isConditional()) return; - BasicBlock::iterator I = LTerm; + BasicBlock::iterator I = LTerm->getIterator(); if (I == LStart->getParent()->begin()) return; --I; if (!isa<CallInst>(*I)) return; @@ -612,7 +612,7 @@ void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart, tryUnify(LTerm->getSuccessor(0), RInvoke->getNormalDest()); } else if (isa<InvokeInst>(LTerm) && isa<BranchInst>(RTerm)) { if (cast<BranchInst>(RTerm)->isConditional()) return; - BasicBlock::iterator I = RTerm; + BasicBlock::iterator I = RTerm->getIterator(); if (I == RStart->getParent()->begin()) return; --I; if (!isa<CallInst>(*I)) return; diff --git a/tools/llvm-dis/llvm-dis.cpp b/tools/llvm-dis/llvm-dis.cpp index 4b7d94d5b261..9fdfcd4256c9 100644 --- a/tools/llvm-dis/llvm-dis.cpp +++ b/tools/llvm-dis/llvm-dis.cpp @@ -159,7 +159,7 @@ int main(int argc, char **argv) { ErrorOr<std::unique_ptr<Module>> MOrErr = getStreamedBitcodeModule(DisplayFilename, std::move(Streamer), Context); M = std::move(*MOrErr); - M->materializeAllPermanently(); + M->materializeAll(); } else { errs() << argv[0] << ": " << ErrorMessage << '\n'; return 1; diff --git a/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/tools/llvm-dwarfdump/llvm-dwarfdump.cpp index db3fcf6ee7bd..eaacc7c5f21b 100644 --- a/tools/llvm-dwarfdump/llvm-dwarfdump.cpp +++ b/tools/llvm-dwarfdump/llvm-dwarfdump.cpp @@ -15,6 +15,7 @@ #include "llvm/ADT/Triple.h" #include "llvm/DebugInfo/DIContext.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/Object/MachOUniversal.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Object/RelocVisitor.h" #include "llvm/Support/CommandLine.h" @@ -22,6 +23,7 @@ #include "llvm/Support/Format.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" @@ -35,19 +37,19 @@ using namespace llvm; using namespace object; static cl::list<std::string> -InputFilenames(cl::Positional, cl::desc("<input object files>"), +InputFilenames(cl::Positional, cl::desc("<input object files or .dSYM bundles>"), cl::ZeroOrMore); -static cl::opt<DIDumpType> -DumpType("debug-dump", cl::init(DIDT_All), - cl::desc("Dump of debug sections:"), - cl::values( +static cl::opt<DIDumpType> DumpType( + "debug-dump", cl::init(DIDT_All), cl::desc("Dump of debug sections:"), + cl::values( 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_AppleNamespaces, "apple_namespaces", + ".apple_namespaces"), clEnumValN(DIDT_AppleObjC, "apple_objc", ".apple_objc"), clEnumValN(DIDT_Aranges, "aranges", ".debug_aranges"), clEnumValN(DIDT_Info, "info", ".debug_info"), @@ -59,6 +61,7 @@ DumpType("debug-dump", cl::init(DIDT_All), clEnumValN(DIDT_Loc, "loc", ".debug_loc"), clEnumValN(DIDT_LocDwo, "loc.dwo", ".debug_loc.dwo"), clEnumValN(DIDT_Frames, "frames", ".debug_frame"), + clEnumValN(DIDT_Macro, "macro", ".debug_macinfo"), clEnumValN(DIDT_Ranges, "ranges", ".debug_ranges"), clEnumValN(DIDT_Pubnames, "pubnames", ".debug_pubnames"), clEnumValN(DIDT_Pubtypes, "pubtypes", ".debug_pubtypes"), @@ -66,38 +69,79 @@ DumpType("debug-dump", cl::init(DIDT_All), clEnumValN(DIDT_GnuPubtypes, "gnu_pubtypes", ".debug_gnu_pubtypes"), clEnumValN(DIDT_Str, "str", ".debug_str"), clEnumValN(DIDT_StrDwo, "str.dwo", ".debug_str.dwo"), - clEnumValN(DIDT_StrOffsetsDwo, "str_offsets.dwo", ".debug_str_offsets.dwo"), - clEnumValEnd)); + clEnumValN(DIDT_StrOffsetsDwo, "str_offsets.dwo", + ".debug_str_offsets.dwo"), + clEnumValN(DIDT_CUIndex, "cu_index", ".debug_cu_index"), + clEnumValN(DIDT_TUIndex, "tu_index", ".debug_tu_index"), clEnumValEnd)); -static int ReturnValue = EXIT_SUCCESS; - -static bool error(StringRef Filename, std::error_code EC) { +static void error(StringRef Filename, std::error_code EC) { if (!EC) - return false; + return; errs() << Filename << ": " << EC.message() << "\n"; - ReturnValue = EXIT_FAILURE; - return true; + exit(1); +} + +static void DumpObjectFile(ObjectFile &Obj, Twine Filename) { + std::unique_ptr<DIContext> DICtx(new DWARFContextInMemory(Obj)); + + outs() << Filename.str() << ":\tfile format " << Obj.getFileFormatName() + << "\n\n"; + // Dump the complete DWARF structure. + DICtx->dump(outs(), DumpType); } static void DumpInput(StringRef Filename) { ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr = MemoryBuffer::getFileOrSTDIN(Filename); - if (error(Filename, BuffOrErr.getError())) - return; + error(Filename, BuffOrErr.getError()); std::unique_ptr<MemoryBuffer> Buff = std::move(BuffOrErr.get()); - ErrorOr<std::unique_ptr<ObjectFile>> ObjOrErr = - ObjectFile::createObjectFile(Buff->getMemBufferRef()); - if (error(Filename, ObjOrErr.getError())) - return; - ObjectFile &Obj = *ObjOrErr.get(); + ErrorOr<std::unique_ptr<Binary>> BinOrErr = + object::createBinary(Buff->getMemBufferRef()); + error(Filename, BinOrErr.getError()); - std::unique_ptr<DIContext> DICtx(new DWARFContextInMemory(Obj)); + if (auto *Obj = dyn_cast<ObjectFile>(BinOrErr->get())) + DumpObjectFile(*Obj, Filename); + else if (auto *Fat = dyn_cast<MachOUniversalBinary>(BinOrErr->get())) + for (auto &ObjForArch : Fat->objects()) { + auto MachOOrErr = ObjForArch.getAsObjectFile(); + error(Filename, MachOOrErr.getError()); + DumpObjectFile(**MachOOrErr, + Filename + " (" + ObjForArch.getArchTypeName() + ")"); + } +} - outs() << Filename - << ":\tfile format " << Obj.getFileFormatName() << "\n\n"; - // Dump the complete DWARF structure. - DICtx->dump(outs(), DumpType); +/// If the input path is a .dSYM bundle (as created by the dsymutil tool), +/// replace it with individual entries for each of the object files inside the +/// bundle otherwise return the input path. +static std::vector<std::string> expandBundle(std::string InputPath) { + std::vector<std::string> BundlePaths; + SmallString<256> BundlePath(InputPath); + // Manually open up the bundle to avoid introducing additional dependencies. + if (sys::fs::is_directory(BundlePath) && + sys::path::extension(BundlePath) == ".dSYM") { + std::error_code EC; + sys::path::append(BundlePath, "Contents", "Resources", "DWARF"); + for (sys::fs::directory_iterator Dir(BundlePath, EC), DirEnd; + Dir != DirEnd && !EC; Dir.increment(EC)) { + const std::string &Path = Dir->path(); + sys::fs::file_status Status; + EC = sys::fs::status(Path, Status); + error(Path, EC); + switch (Status.type()) { + case sys::fs::file_type::regular_file: + case sys::fs::file_type::symlink_file: + case sys::fs::file_type::type_unknown: + BundlePaths.push_back(Path); + break; + default: /*ignore*/; + } + } + error(BundlePath, EC); + } + if (!BundlePaths.size()) + BundlePaths.push_back(InputPath); + return BundlePaths; } int main(int argc, char **argv) { @@ -112,7 +156,14 @@ int main(int argc, char **argv) { if (InputFilenames.size() == 0) InputFilenames.push_back("a.out"); - std::for_each(InputFilenames.begin(), InputFilenames.end(), DumpInput); + // Expand any .dSYM bundles to the individual object files contained therein. + std::vector<std::string> Objects; + for (auto F : InputFilenames) { + auto Objs = expandBundle(F); + Objects.insert(Objects.end(), Objs.begin(), Objs.end()); + } + + std::for_each(Objects.begin(), Objects.end(), DumpInput); - return ReturnValue; + return EXIT_SUCCESS; } diff --git a/tools/llvm-dwp/CMakeLists.txt b/tools/llvm-dwp/CMakeLists.txt new file mode 100644 index 000000000000..b29c00d49c3d --- /dev/null +++ b/tools/llvm-dwp/CMakeLists.txt @@ -0,0 +1,13 @@ +set(LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + AsmPrinter + DebugInfoDWARF + MC + Object + Support + Target + ) + +add_llvm_tool(llvm-dwp + llvm-dwp.cpp + ) diff --git a/tools/llvm-dwp/LLVMBuild.txt b/tools/llvm-dwp/LLVMBuild.txt new file mode 100644 index 000000000000..345a73757255 --- /dev/null +++ b/tools/llvm-dwp/LLVMBuild.txt @@ -0,0 +1,23 @@ +;===- ./tools/llvm-dwp/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-dwp +parent = Tools +required_libraries = AsmPrinter DebugInfoDWARF MC Object Support all-targets + diff --git a/tools/llvm-dwp/Makefile b/tools/llvm-dwp/Makefile new file mode 100644 index 000000000000..826371ecf915 --- /dev/null +++ b/tools/llvm-dwp/Makefile @@ -0,0 +1,18 @@ +##===- tools/llvm-dwp/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-dwp +LINK_COMPONENTS := all-targets AsmPrinter DebugInfoDWARF MC Object Support + +# This tool has no plugins, optimize startup time. +TOOL_NO_EXPORTS := 1 + +include $(LEVEL)/Makefile.common + diff --git a/tools/llvm-dwp/llvm-dwp.cpp b/tools/llvm-dwp/llvm-dwp.cpp new file mode 100644 index 000000000000..570854b849c1 --- /dev/null +++ b/tools/llvm-dwp/llvm-dwp.cpp @@ -0,0 +1,420 @@ +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFUnitIndex.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSectionELF.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCTargetOptionsCommandFlags.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Options.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Target/TargetMachine.h" +#include <list> +#include <memory> +#include <unordered_set> + +using namespace llvm; +using namespace llvm::object; +using namespace cl; + +OptionCategory DwpCategory("Specific Options"); +static list<std::string> InputFiles(Positional, OneOrMore, + desc("<input files>"), cat(DwpCategory)); + +static opt<std::string> OutputFilename(Required, "o", + desc("Specify the output file."), + value_desc("filename"), + cat(DwpCategory)); + +static int error(const Twine &Error, const Twine &Context) { + errs() << Twine("while processing ") + Context + ":\n"; + errs() << Twine("error: ") + Error + "\n"; + return 1; +} + +static std::error_code +writeStringsAndOffsets(MCStreamer &Out, StringMap<uint32_t> &Strings, + uint32_t &StringOffset, MCSection *StrSection, + MCSection *StrOffsetSection, StringRef CurStrSection, + StringRef CurStrOffsetSection) { + // Could possibly produce an error or warning if one of these was non-null but + // the other was null. + if (CurStrSection.empty() || CurStrOffsetSection.empty()) + return std::error_code(); + + DenseMap<uint32_t, uint32_t> OffsetRemapping; + + DataExtractor Data(CurStrSection, true, 0); + uint32_t LocalOffset = 0; + uint32_t PrevOffset = 0; + while (const char *s = Data.getCStr(&LocalOffset)) { + StringRef Str(s, LocalOffset - PrevOffset - 1); + auto Pair = Strings.insert(std::make_pair(Str, StringOffset)); + if (Pair.second) { + Out.SwitchSection(StrSection); + Out.EmitBytes( + StringRef(Pair.first->getKeyData(), Pair.first->getKeyLength() + 1)); + StringOffset += Str.size() + 1; + } + OffsetRemapping[PrevOffset] = Pair.first->second; + PrevOffset = LocalOffset; + } + + Data = DataExtractor(CurStrOffsetSection, true, 0); + + Out.SwitchSection(StrOffsetSection); + + uint32_t Offset = 0; + uint64_t Size = CurStrOffsetSection.size(); + while (Offset < Size) { + auto OldOffset = Data.getU32(&Offset); + auto NewOffset = OffsetRemapping[OldOffset]; + Out.EmitIntValue(NewOffset, 4); + } + + return std::error_code(); +} + +static uint32_t getCUAbbrev(StringRef Abbrev, uint64_t AbbrCode) { + uint64_t CurCode; + uint32_t Offset = 0; + DataExtractor AbbrevData(Abbrev, true, 0); + while ((CurCode = AbbrevData.getULEB128(&Offset)) != AbbrCode) { + // Tag + AbbrevData.getULEB128(&Offset); + // DW_CHILDREN + AbbrevData.getU8(&Offset); + // Attributes + while (AbbrevData.getULEB128(&Offset) | AbbrevData.getULEB128(&Offset)) + ; + } + return Offset; +} + +static uint64_t getCUSignature(StringRef Abbrev, StringRef Info) { + uint32_t Offset = 0; + DataExtractor InfoData(Info, true, 0); + InfoData.getU32(&Offset); // Length + uint16_t Version = InfoData.getU16(&Offset); + InfoData.getU32(&Offset); // Abbrev offset (should be zero) + uint8_t AddrSize = InfoData.getU8(&Offset); + + uint32_t AbbrCode = InfoData.getULEB128(&Offset); + + DataExtractor AbbrevData(Abbrev, true, 0); + uint32_t AbbrevOffset = getCUAbbrev(Abbrev, AbbrCode); + uint64_t Tag = AbbrevData.getULEB128(&AbbrevOffset); + (void)Tag; + // FIXME: Real error handling + assert(Tag == dwarf::DW_TAG_compile_unit); + // DW_CHILDREN + AbbrevData.getU8(&AbbrevOffset); + uint32_t Name; + uint32_t Form; + while ((Name = AbbrevData.getULEB128(&AbbrevOffset)) | + (Form = AbbrevData.getULEB128(&AbbrevOffset)) && + Name != dwarf::DW_AT_GNU_dwo_id) { + DWARFFormValue::skipValue(Form, InfoData, &Offset, Version, AddrSize); + } + // FIXME: Real error handling + assert(Name == dwarf::DW_AT_GNU_dwo_id); + return InfoData.getU64(&Offset); +} + +struct UnitIndexEntry { + uint64_t Signature; + DWARFUnitIndex::Entry::SectionContribution Contributions[8]; +}; + +static void addAllTypes(MCStreamer &Out, + std::vector<UnitIndexEntry> &TypeIndexEntries, + MCSection *OutputTypes, StringRef Types, + const UnitIndexEntry &CUEntry, uint32_t &TypesOffset) { + if (Types.empty()) + return; + + Out.SwitchSection(OutputTypes); + uint32_t Offset = 0; + DataExtractor Data(Types, true, 0); + while (Data.isValidOffset(Offset)) { + UnitIndexEntry Entry = CUEntry; + // Zero out the debug_info contribution + Entry.Contributions[0] = {}; + auto &C = Entry.Contributions[DW_SECT_TYPES - DW_SECT_INFO]; + C.Offset = TypesOffset; + auto PrevOffset = Offset; + // Length of the unit, including the 4 byte length field. + C.Length = Data.getU32(&Offset) + 4; + + Data.getU16(&Offset); // Version + Data.getU32(&Offset); // Abbrev offset + Data.getU8(&Offset); // Address size + Entry.Signature = Data.getU64(&Offset); + Offset = PrevOffset + C.Length; + + if (any_of(TypeIndexEntries, [&](const UnitIndexEntry &E) { + return E.Signature == Entry.Signature; + })) + continue; + + Out.EmitBytes(Types.substr(PrevOffset, C.Length)); + TypesOffset += C.Length; + + TypeIndexEntries.push_back(Entry); + } +} + +static void +writeIndexTable(MCStreamer &Out, ArrayRef<unsigned> ContributionOffsets, + ArrayRef<UnitIndexEntry> IndexEntries, + uint32_t DWARFUnitIndex::Entry::SectionContribution::*Field) { + for (const auto &E : IndexEntries) + for (size_t i = 0; i != array_lengthof(E.Contributions); ++i) + if (ContributionOffsets[i]) + Out.EmitIntValue(E.Contributions[i].*Field, 4); +} + +static void writeIndex(MCStreamer &Out, MCSection *Section, + ArrayRef<unsigned> ContributionOffsets, + ArrayRef<UnitIndexEntry> IndexEntries) { + unsigned Columns = 0; + for (auto &C : ContributionOffsets) + if (C) + ++Columns; + + std::vector<unsigned> Buckets(NextPowerOf2(3 * IndexEntries.size() / 2)); + uint64_t Mask = Buckets.size() - 1; + for (size_t i = 0; i != IndexEntries.size(); ++i) { + auto S = IndexEntries[i].Signature; + auto H = S & Mask; + while (Buckets[H]) { + assert(S != IndexEntries[Buckets[H] - 1].Signature && + "Duplicate type unit"); + H += ((S >> 32) & Mask) | 1; + } + Buckets[H] = i + 1; + } + + Out.SwitchSection(Section); + Out.EmitIntValue(2, 4); // Version + Out.EmitIntValue(Columns, 4); // Columns + Out.EmitIntValue(IndexEntries.size(), 4); // Num Units + Out.EmitIntValue(Buckets.size(), 4); // Num Buckets + + // Write the signatures. + for (const auto &I : Buckets) + Out.EmitIntValue(I ? IndexEntries[I - 1].Signature : 0, 8); + + // Write the indexes. + for (const auto &I : Buckets) + Out.EmitIntValue(I, 4); + + // Write the column headers (which sections will appear in the table) + for (size_t i = 0; i != ContributionOffsets.size(); ++i) + if (ContributionOffsets[i]) + Out.EmitIntValue(i + DW_SECT_INFO, 4); + + // Write the offsets. + writeIndexTable(Out, ContributionOffsets, IndexEntries, + &DWARFUnitIndex::Entry::SectionContribution::Offset); + + // Write the lengths. + writeIndexTable(Out, ContributionOffsets, IndexEntries, + &DWARFUnitIndex::Entry::SectionContribution::Length); +} +static std::error_code write(MCStreamer &Out, ArrayRef<std::string> Inputs) { + const auto &MCOFI = *Out.getContext().getObjectFileInfo(); + MCSection *const StrSection = MCOFI.getDwarfStrDWOSection(); + MCSection *const StrOffsetSection = MCOFI.getDwarfStrOffDWOSection(); + MCSection *const TypesSection = MCOFI.getDwarfTypesDWOSection(); + const StringMap<std::pair<MCSection *, DWARFSectionKind>> KnownSections = { + {"debug_info.dwo", {MCOFI.getDwarfInfoDWOSection(), DW_SECT_INFO}}, + {"debug_types.dwo", {MCOFI.getDwarfTypesDWOSection(), DW_SECT_TYPES}}, + {"debug_str_offsets.dwo", {StrOffsetSection, DW_SECT_STR_OFFSETS}}, + {"debug_str.dwo", {StrSection, static_cast<DWARFSectionKind>(0)}}, + {"debug_loc.dwo", {MCOFI.getDwarfLocDWOSection(), DW_SECT_LOC}}, + {"debug_line.dwo", {MCOFI.getDwarfLineDWOSection(), DW_SECT_LINE}}, + {"debug_abbrev.dwo", {MCOFI.getDwarfAbbrevDWOSection(), DW_SECT_ABBREV}}}; + + std::vector<UnitIndexEntry> IndexEntries; + std::vector<UnitIndexEntry> TypeIndexEntries; + + StringMap<uint32_t> Strings; + uint32_t StringOffset = 0; + + uint32_t ContributionOffsets[8] = {}; + + for (const auto &Input : Inputs) { + auto ErrOrObj = object::ObjectFile::createObjectFile(Input); + if (!ErrOrObj) + return ErrOrObj.getError(); + + IndexEntries.emplace_back(); + UnitIndexEntry &CurEntry = IndexEntries.back(); + + StringRef CurStrSection; + StringRef CurStrOffsetSection; + StringRef CurTypesSection; + StringRef InfoSection; + StringRef AbbrevSection; + + for (const auto &Section : ErrOrObj->getBinary()->sections()) { + StringRef Name; + if (std::error_code Err = Section.getName(Name)) + return Err; + + auto SectionPair = + KnownSections.find(Name.substr(Name.find_first_not_of("._"))); + if (SectionPair == KnownSections.end()) + continue; + + StringRef Contents; + if (auto Err = Section.getContents(Contents)) + return Err; + + if (DWARFSectionKind Kind = SectionPair->second.second) { + auto Index = Kind - DW_SECT_INFO; + if (Kind != DW_SECT_TYPES) { + CurEntry.Contributions[Index].Offset = ContributionOffsets[Index]; + ContributionOffsets[Index] += + (CurEntry.Contributions[Index].Length = Contents.size()); + } + + switch (Kind) { + case DW_SECT_INFO: + InfoSection = Contents; + break; + case DW_SECT_ABBREV: + AbbrevSection = Contents; + break; + default: + break; + } + } + + MCSection *OutSection = SectionPair->second.first; + if (OutSection == StrOffsetSection) + CurStrOffsetSection = Contents; + else if (OutSection == StrSection) + CurStrSection = Contents; + else if (OutSection == TypesSection) + CurTypesSection = Contents; + else { + Out.SwitchSection(OutSection); + Out.EmitBytes(Contents); + } + } + + assert(!AbbrevSection.empty()); + assert(!InfoSection.empty()); + CurEntry.Signature = getCUSignature(AbbrevSection, InfoSection); + addAllTypes(Out, TypeIndexEntries, TypesSection, CurTypesSection, CurEntry, + ContributionOffsets[DW_SECT_TYPES - DW_SECT_INFO]); + + if (auto Err = writeStringsAndOffsets(Out, Strings, StringOffset, + StrSection, StrOffsetSection, + CurStrSection, CurStrOffsetSection)) + return Err; + } + + if (!TypeIndexEntries.empty()) { + // Lie about there being no info contributions so the TU index only includes + // the type unit contribution + ContributionOffsets[0] = 0; + writeIndex(Out, MCOFI.getDwarfTUIndexSection(), ContributionOffsets, + TypeIndexEntries); + } + + // Lie about the type contribution + ContributionOffsets[DW_SECT_TYPES - DW_SECT_INFO] = 0; + // Unlie about the info contribution + ContributionOffsets[0] = 1; + + writeIndex(Out, MCOFI.getDwarfCUIndexSection(), ContributionOffsets, + IndexEntries); + + return std::error_code(); +} + +int main(int argc, char **argv) { + + ParseCommandLineOptions(argc, argv, "merge split dwarf (.dwo) files"); + + llvm::InitializeAllTargetInfos(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllTargets(); + llvm::InitializeAllAsmPrinters(); + + std::string ErrorStr; + StringRef Context = "dwarf streamer init"; + + Triple TheTriple("x86_64-linux-gnu"); + + // Get the target. + const Target *TheTarget = + TargetRegistry::lookupTarget("", TheTriple, ErrorStr); + if (!TheTarget) + return error(ErrorStr, Context); + std::string TripleName = TheTriple.getTriple(); + + // Create all the MC Objects. + std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName)); + if (!MRI) + return error(Twine("no register info for target ") + TripleName, Context); + + std::unique_ptr<MCAsmInfo> MAI(TheTarget->createMCAsmInfo(*MRI, TripleName)); + if (!MAI) + return error("no asm info for target " + TripleName, Context); + + MCObjectFileInfo MOFI; + MCContext MC(MAI.get(), MRI.get(), &MOFI); + MOFI.InitMCObjectFileInfo(TheTriple, Reloc::Default, CodeModel::Default, MC); + + auto MAB = TheTarget->createMCAsmBackend(*MRI, TripleName, ""); + if (!MAB) + return error("no asm backend for target " + TripleName, Context); + + std::unique_ptr<MCInstrInfo> MII(TheTarget->createMCInstrInfo()); + if (!MII) + return error("no instr info info for target " + TripleName, Context); + + std::unique_ptr<MCSubtargetInfo> MSTI( + TheTarget->createMCSubtargetInfo(TripleName, "", "")); + if (!MSTI) + return error("no subtarget info for target " + TripleName, Context); + + MCCodeEmitter *MCE = TheTarget->createMCCodeEmitter(*MII, *MRI, MC); + if (!MCE) + return error("no code emitter for target " + TripleName, Context); + + // Create the output file. + std::error_code EC; + raw_fd_ostream OutFile(OutputFilename, EC, sys::fs::F_None); + if (EC) + return error(Twine(OutputFilename) + ": " + EC.message(), Context); + + MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags(); + std::unique_ptr<MCStreamer> MS(TheTarget->createMCObjectStreamer( + TheTriple, MC, *MAB, OutFile, MCE, *MSTI, MCOptions.MCRelaxAll, + MCOptions.MCIncrementalLinkerCompatible, + /*DWARFMustBeAtTheEnd*/ false)); + if (!MS) + return error("no object streamer for target " + TripleName, Context); + + if (auto Err = write(*MS, InputFiles)) + return error(Err.message(), "Writing DWP file"); + + MS->Finish(); +} diff --git a/tools/llvm-extract/llvm-extract.cpp b/tools/llvm-extract/llvm-extract.cpp index 936496cd7fe6..1da456d33f52 100644 --- a/tools/llvm-extract/llvm-extract.cpp +++ b/tools/llvm-extract/llvm-extract.cpp @@ -222,45 +222,42 @@ int main(int argc, char **argv) { } } - // Materialize requisite global values. - if (!DeleteFn) - for (size_t i = 0, e = GVs.size(); i != e; ++i) { - GlobalValue *GV = GVs[i]; - if (std::error_code EC = GV->materialize()) { - errs() << argv[0] << ": error reading input: " << EC.message() << "\n"; - return 1; - } + auto Materialize = [&](GlobalValue &GV) { + if (std::error_code EC = GV.materialize()) { + errs() << argv[0] << ": error reading input: " << EC.message() << "\n"; + exit(1); } - else { + }; + + // Materialize requisite global values. + if (!DeleteFn) { + for (size_t i = 0, e = GVs.size(); i != e; ++i) + Materialize(*GVs[i]); + } 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)) { - 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)) { - if (std::error_code EC = F.materialize()) { - errs() << argv[0] << ": error reading input: " << EC.message() - << "\n"; - return 1; - } - } + if (!GVSet.count(&F)) + Materialize(F); } } + { + std::vector<GlobalValue *> Gvs(GVs.begin(), GVs.end()); + legacy::PassManager Extract; + Extract.add(createGVExtractionPass(Gvs, DeleteFn)); + Extract.run(*M); + + // Now that we have all the GVs we want, mark the module as fully + // materialized. + // FIXME: should the GVExtractionPass handle this? + M->materializeAll(); + } + // In addition to deleting all other functions, we also want to spiff it // up a little bit. Do this now. legacy::PassManager Passes; - std::vector<GlobalValue*> Gvs(GVs.begin(), GVs.end()); - - Passes.add(createGVExtractionPass(Gvs, DeleteFn)); if (!DeleteFn) Passes.add(createGlobalDCEPass()); // Delete unreachable globals Passes.add(createStripDeadDebugInfoPass()); // Remove dead debug info diff --git a/tools/llvm-go/llvm-go.go b/tools/llvm-go/llvm-go.go index c5c3fd244cad..ed79ca67b89d 100644 --- a/tools/llvm-go/llvm-go.go +++ b/tools/llvm-go/llvm-go.go @@ -24,6 +24,11 @@ import ( "strings" ) +const ( + linkmodeComponentLibs = "component-libs" + linkmodeDylib = "dylib" +) + type pkg struct { llvmpath, pkgpath string } @@ -66,11 +71,12 @@ var components = []string{ 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" + bin, _ := filepath.Split(os.Args[0]) + configpath = filepath.Join(bin, "llvm-config") } cmd := exec.Command(configpath, args...) + cmd.Stderr = os.Stderr out, err := cmd.Output() if err != nil { panic(err.Error()) @@ -78,11 +84,21 @@ func llvmConfig(args ...string) string { outstr := string(out) outstr = strings.TrimSuffix(outstr, "\n") - return strings.Replace(outstr, "\n", " ", -1) + outstr = strings.Replace(outstr, "\n", " ", -1) + return outstr } -func llvmFlags() compilerFlags { - ldflags := llvmConfig(append([]string{"--ldflags", "--libs", "--system-libs"}, components...)...) +func llvmFlags(linkmode string) compilerFlags { + ldflags := llvmConfig("--ldflags") + switch linkmode { + case linkmodeComponentLibs: + ldflags += " " + llvmConfig(append([]string{"--libs"}, components...)...) + case linkmodeDylib: + ldflags += " -lLLVM" + default: + panic("invalid linkmode: " + linkmode) + } + ldflags += " " + llvmConfig("--system-libs") if runtime.GOOS != "darwin" { // OS X doesn't like -rpath with cgo. See: // https://code.google.com/p/go/issues/detail?id=7293 @@ -117,8 +133,8 @@ func printComponents() { fmt.Println(strings.Join(components, " ")) } -func printConfig() { - flags := llvmFlags() +func printConfig(linkmode string) { + flags := llvmFlags(linkmode) fmt.Printf(`// +build !byollvm @@ -137,7 +153,7 @@ type (run_build_sh int) `, flags.cpp, flags.cxx, flags.ld) } -func runGoWithLLVMEnv(args []string, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags string) { +func runGoWithLLVMEnv(args []string, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags, linkmode string) { args = addTag(args, "byollvm") srcdir := llvmConfig("--src-root") @@ -166,7 +182,7 @@ func runGoWithLLVMEnv(args []string, cc, cxx, gocmd, llgo, cppflags, cxxflags, l newgopathlist = append(newgopathlist, filepath.SplitList(os.Getenv("GOPATH"))...) newgopath := strings.Join(newgopathlist, string(filepath.ListSeparator)) - flags := llvmFlags() + flags := llvmFlags(linkmode) newenv := []string{ "CC=" + cc, @@ -178,7 +194,7 @@ func runGoWithLLVMEnv(args []string, cc, cxx, gocmd, llgo, cppflags, cxxflags, l "PATH=" + newpath, } if llgo != "" { - newenv = append(newenv, "GCCGO=" + llgo) + newenv = append(newenv, "GCCGO="+llgo) } for _, v := range os.Environ() { @@ -234,45 +250,44 @@ func main() { ldflags := os.Getenv("CGO_LDFLAGS") gocmd := "go" llgo := "" + linkmode := linkmodeComponentLibs + + flags := []struct { + name string + dest *string + }{ + {"cc", &cc}, + {"cxx", &cxx}, + {"go", &gocmd}, + {"llgo", &llgo}, + {"cppflags", &cppflags}, + {"ldflags", &ldflags}, + {"linkmode", &linkmode}, + } args := os.Args[1:] - DONE: for { - switch { - case len(args) == 0: +LOOP: + for { + if 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], "go="): - gocmd = args[0][3:] - args = args[1:] - case strings.HasPrefix(args[0], "llgo="): - llgo = args[0][5:] - 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 } + for _, flag := range flags { + if strings.HasPrefix(args[0], flag.name+"=") { + *flag.dest = args[0][len(flag.name)+1:] + args = args[1:] + continue LOOP + } + } + break } switch args[0] { case "build", "get", "install", "run", "test": - runGoWithLLVMEnv(args, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags) + runGoWithLLVMEnv(args, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags, linkmode) case "print-components": printComponents() case "print-config": - printConfig() + printConfig(linkmode) default: usage() } diff --git a/tools/llvm-link/CMakeLists.txt b/tools/llvm-link/CMakeLists.txt index d4f5be749046..e4ced186336c 100644 --- a/tools/llvm-link/CMakeLists.txt +++ b/tools/llvm-link/CMakeLists.txt @@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS Core IRReader Linker + Object Support ) diff --git a/tools/llvm-link/LLVMBuild.txt b/tools/llvm-link/LLVMBuild.txt index 2e386f3c2316..807823820ed6 100644 --- a/tools/llvm-link/LLVMBuild.txt +++ b/tools/llvm-link/LLVMBuild.txt @@ -19,4 +19,4 @@ type = Tool name = llvm-link parent = Tools -required_libraries = AsmParser BitReader BitWriter IRReader Linker +required_libraries = AsmParser BitReader BitWriter IRReader Linker Object diff --git a/tools/llvm-link/Makefile b/tools/llvm-link/Makefile index ed30d2d256b8..a08d2844ceca 100644 --- a/tools/llvm-link/Makefile +++ b/tools/llvm-link/Makefile @@ -9,7 +9,7 @@ LEVEL := ../.. TOOLNAME := llvm-link -LINK_COMPONENTS := linker bitreader bitwriter asmparser irreader +LINK_COMPONENTS := linker bitreader bitwriter asmparser irreader object # This tool has no plugins, optimize startup time. TOOL_NO_EXPORTS := 1 diff --git a/tools/llvm-link/llvm-link.cpp b/tools/llvm-link/llvm-link.cpp index 369f3477fe5c..a32383028ae2 100644 --- a/tools/llvm-link/llvm-link.cpp +++ b/tools/llvm-link/llvm-link.cpp @@ -18,10 +18,12 @@ #include "llvm/IR/AutoUpgrade.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticPrinter.h" +#include "llvm/IR/FunctionInfo.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IR/Verifier.h" #include "llvm/IRReader/IRReader.h" +#include "llvm/Object/FunctionIndexObjectFile.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/ManagedStatic.h" @@ -43,11 +45,34 @@ static cl::list<std::string> OverridingInputs( cl::desc( "input bitcode file which can override previously defined symbol(s)")); +// Option to simulate function importing for testing. This enables using +// llvm-link to simulate ThinLTO backend processes. +static cl::list<std::string> Imports( + "import", cl::ZeroOrMore, cl::value_desc("function:filename"), + cl::desc("Pair of function name and filename, where function should be " + "imported from bitcode in filename")); + +// Option to support testing of function importing. The function index +// must be specified in the case were we request imports via the -import +// option, as well as when compiling any module with functions that may be +// exported (imported by a different llvm-link -import invocation), to ensure +// consistent promotion and renaming of locals. +static cl::opt<std::string> FunctionIndex("functionindex", + cl::desc("Function index filename"), + cl::init(""), + cl::value_desc("filename")); + static cl::opt<std::string> OutputFilename("o", cl::desc("Override output filename"), cl::init("-"), cl::value_desc("filename")); static cl::opt<bool> +Internalize("internalize", cl::desc("Internalize linked symbols")); + +static cl::opt<bool> +OnlyNeeded("only-needed", cl::desc("Link only needed symbols")); + +static cl::opt<bool> Force("f", cl::desc("Enable binary output on terminals")); static cl::opt<bool> @@ -64,6 +89,10 @@ static cl::opt<bool> SuppressWarnings("suppress-warnings", cl::desc("Suppress all linking warnings"), cl::init(false)); +static cl::opt<bool> + PreserveModules("preserve-modules", + cl::desc("Preserve linked modules for testing")); + static cl::opt<bool> PreserveBitcodeUseListOrder( "preserve-bc-uselistorder", cl::desc("Preserve use-list order when writing LLVM bitcode."), @@ -77,16 +106,21 @@ static cl::opt<bool> PreserveAssemblyUseListOrder( // 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 std::unique_ptr<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, + bool MaterializeMetadata = true) { SMDiagnostic Err; if (Verbose) errs() << "Loading '" << FN << "'\n"; - std::unique_ptr<Module> Result = getLazyIRFileModule(FN, Err, Context); + std::unique_ptr<Module> Result = + getLazyIRFileModule(FN, Err, Context, !MaterializeMetadata); if (!Result) Err.print(argv0, errs()); - Result->materializeMetadata(); - UpgradeDebugInfo(*Result); + if (MaterializeMetadata) { + Result->materializeMetadata(); + UpgradeDebugInfo(*Result); + } return Result; } @@ -112,9 +146,111 @@ static void diagnosticHandler(const DiagnosticInfo &DI) { errs() << '\n'; } +static void diagnosticHandlerWithContext(const DiagnosticInfo &DI, void *C) { + diagnosticHandler(DI); +} + +/// Import any functions requested via the -import option. +static bool importFunctions(const char *argv0, LLVMContext &Context, + Linker &L) { + StringMap<std::unique_ptr<DenseMap<unsigned, MDNode *>>> + ModuleToTempMDValsMap; + for (const auto &Import : Imports) { + // Identify the requested function and its bitcode source file. + size_t Idx = Import.find(':'); + if (Idx == std::string::npos) { + errs() << "Import parameter bad format: " << Import << "\n"; + return false; + } + std::string FunctionName = Import.substr(0, Idx); + std::string FileName = Import.substr(Idx + 1, std::string::npos); + + // Load the specified source module. + std::unique_ptr<Module> M = loadFile(argv0, FileName, Context, false); + if (!M.get()) { + errs() << argv0 << ": error loading file '" << FileName << "'\n"; + return false; + } + + if (verifyModule(*M, &errs())) { + errs() << argv0 << ": " << FileName + << ": error: input module is broken!\n"; + return false; + } + + Function *F = M->getFunction(FunctionName); + if (!F) { + errs() << "Ignoring import request for non-existent function " + << FunctionName << " from " << FileName << "\n"; + continue; + } + // We cannot import weak_any functions without possibly affecting the + // order they are seen and selected by the linker, changing program + // semantics. + if (F->hasWeakAnyLinkage()) { + errs() << "Ignoring import request for weak-any function " << FunctionName + << " from " << FileName << "\n"; + continue; + } + + if (Verbose) + errs() << "Importing " << FunctionName << " from " << FileName << "\n"; + + std::unique_ptr<FunctionInfoIndex> Index; + if (!FunctionIndex.empty()) { + ErrorOr<std::unique_ptr<FunctionInfoIndex>> IndexOrErr = + llvm::getFunctionIndexForFile(FunctionIndex, diagnosticHandler); + std::error_code EC = IndexOrErr.getError(); + if (EC) { + errs() << EC.message() << '\n'; + return false; + } + Index = std::move(IndexOrErr.get()); + } + + // Save the mapping of value ids to temporary metadata created when + // importing this function. If we have already imported from this module, + // add new temporary metadata to the existing mapping. + auto &TempMDVals = ModuleToTempMDValsMap[FileName]; + if (!TempMDVals) + TempMDVals = llvm::make_unique<DenseMap<unsigned, MDNode *>>(); + + // Link in the specified function. + DenseSet<const GlobalValue *> FunctionsToImport; + FunctionsToImport.insert(F); + if (L.linkInModule(std::move(M), Linker::Flags::None, Index.get(), + &FunctionsToImport, TempMDVals.get())) + return false; + } + + // Now link in metadata for all modules from which we imported functions. + for (StringMapEntry<std::unique_ptr<DenseMap<unsigned, MDNode *>>> &SME : + ModuleToTempMDValsMap) { + // Load the specified source module. + std::unique_ptr<Module> M = loadFile(argv0, SME.getKey(), Context, true); + if (!M.get()) { + errs() << argv0 << ": error loading file '" << SME.getKey() << "'\n"; + return false; + } + + if (verifyModule(*M, &errs())) { + errs() << argv0 << ": " << SME.getKey() + << ": error: input module is broken!\n"; + return false; + } + + // Link in all necessary metadata from this module. + if (L.linkInMetadata(*M, SME.getValue().get())) + return false; + } + return true; +} + static bool linkFiles(const char *argv0, LLVMContext &Context, Linker &L, const cl::list<std::string> &Files, - bool OverrideDuplicateSymbols) { + unsigned Flags) { + // Filter out flags that don't apply to the first file we load. + unsigned ApplicableFlags = Flags & Linker::Flags::OverrideFromSrc; for (const auto &File : Files) { std::unique_ptr<Module> M = loadFile(argv0, File, Context); if (!M.get()) { @@ -127,11 +263,36 @@ static bool linkFiles(const char *argv0, LLVMContext &Context, Linker &L, return false; } + // If a function index is supplied, load it so linkInModule can treat + // local functions/variables as exported and promote if necessary. + std::unique_ptr<FunctionInfoIndex> Index; + if (!FunctionIndex.empty()) { + ErrorOr<std::unique_ptr<FunctionInfoIndex>> IndexOrErr = + llvm::getFunctionIndexForFile(FunctionIndex, diagnosticHandler); + std::error_code EC = IndexOrErr.getError(); + if (EC) { + errs() << EC.message() << '\n'; + return false; + } + Index = std::move(IndexOrErr.get()); + } + if (Verbose) errs() << "Linking in '" << File << "'\n"; - if (L.linkInModule(M.get(), OverrideDuplicateSymbols)) + if (L.linkInModule(std::move(M), ApplicableFlags, Index.get())) return false; + // All linker flags apply to linking of subsequent files. + ApplicableFlags = Flags; + + // If requested for testing, preserve modules by releasing them from + // the unique_ptr before the are freed. This can help catch any + // cross-module references from e.g. unneeded metadata references + // that aren't properly set to null but instead mapped to the source + // module version. The bitcode writer will assert if it finds any such + // cross-module references. + if (PreserveModules) + M.release(); } return true; @@ -143,18 +304,31 @@ int main(int argc, char **argv) { PrettyStackTraceProgram X(argc, argv); LLVMContext &Context = getGlobalContext(); + Context.setDiagnosticHandler(diagnosticHandlerWithContext, nullptr, true); + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. cl::ParseCommandLineOptions(argc, argv, "llvm linker\n"); auto Composite = make_unique<Module>("llvm-link", Context); - Linker L(Composite.get(), diagnosticHandler); + Linker L(*Composite); + + unsigned Flags = Linker::Flags::None; + if (Internalize) + Flags |= Linker::Flags::InternalizeLinkedSymbols; + if (OnlyNeeded) + Flags |= Linker::Flags::LinkOnlyNeeded; // First add all the regular input files - if (!linkFiles(argv[0], Context, L, InputFilenames, false)) + if (!linkFiles(argv[0], Context, L, InputFilenames, Flags)) return 1; // Next the -override ones. - if (!linkFiles(argv[0], Context, L, OverridingInputs, true)) + if (!linkFiles(argv[0], Context, L, OverridingInputs, + Flags | Linker::Flags::OverrideFromSrc)) + return 1; + + // Import any functions requested via -import + if (!importFunctions(argv[0], Context, L)) return 1; if (DumpAsm) errs() << "Here's the assembly:\n" << *Composite; diff --git a/tools/llvm-lto/CMakeLists.txt b/tools/llvm-lto/CMakeLists.txt index 3ea1aeea9a41..29b3339e6cea 100644 --- a/tools/llvm-lto/CMakeLists.txt +++ b/tools/llvm-lto/CMakeLists.txt @@ -1,7 +1,10 @@ set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} + BitWriter + Core LTO MC + Object Support Target ) diff --git a/tools/llvm-lto/LLVMBuild.txt b/tools/llvm-lto/LLVMBuild.txt index c1613a34c0e4..b36f4a981532 100644 --- a/tools/llvm-lto/LLVMBuild.txt +++ b/tools/llvm-lto/LLVMBuild.txt @@ -19,4 +19,4 @@ type = Tool name = llvm-lto parent = Tools -required_libraries = LTO Support all-targets +required_libraries = BitWriter Core LTO Object Support all-targets diff --git a/tools/llvm-lto/Makefile b/tools/llvm-lto/Makefile index f1801b4b20cc..f8ca7e1cac5d 100644 --- a/tools/llvm-lto/Makefile +++ b/tools/llvm-lto/Makefile @@ -14,6 +14,4 @@ LINK_COMPONENTS := lto ipo scalaropts linker bitreader bitwriter mcdisassembler # This tool has no plugins, optimize startup time. TOOL_NO_EXPORTS := 1 -NO_INSTALL := 1 - include $(LEVEL)/Makefile.common diff --git a/tools/llvm-lto/llvm-lto.cpp b/tools/llvm-lto/llvm-lto.cpp index 08218986f45b..4bc692279b9e 100644 --- a/tools/llvm-lto/llvm-lto.cpp +++ b/tools/llvm-lto/llvm-lto.cpp @@ -13,16 +13,22 @@ //===----------------------------------------------------------------------===// #include "llvm/ADT/StringSet.h" +#include "llvm/Bitcode/ReaderWriter.h" #include "llvm/CodeGen/CommandFlags.h" +#include "llvm/IR/DiagnosticPrinter.h" +#include "llvm/IR/LLVMContext.h" #include "llvm/LTO/LTOCodeGenerator.h" #include "llvm/LTO/LTOModule.h" +#include "llvm/Object/FunctionIndexObjectFile.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Signals.h" #include "llvm/Support/TargetSelect.h" +#include "llvm/Support/ToolOutputFile.h" #include "llvm/Support/raw_ostream.h" +#include <list> using namespace llvm; @@ -34,6 +40,10 @@ OptLevel("O", cl::ZeroOrMore, cl::init('2')); +static cl::opt<bool> DisableVerify( + "disable-verify", cl::init(false), + cl::desc("Do not run the verifier during the optimization pipeline")); + static cl::opt<bool> DisableInline("disable-inlining", cl::init(false), cl::desc("Do not run the inliner pass")); @@ -50,6 +60,14 @@ static cl::opt<bool> UseDiagnosticHandler("use-diagnostic-handler", cl::init(false), cl::desc("Use a diagnostic handler to test the handler interface")); +static cl::opt<bool> + ThinLTO("thinlto", cl::init(false), + cl::desc("Only write combined global index for ThinLTO backends")); + +static cl::opt<bool> +SaveModuleFile("save-merged-module", cl::init(false), + cl::desc("Write merged LTO module to file before CodeGen")); + static cl::list<std::string> InputFilenames(cl::Positional, cl::OneOrMore, cl::desc("<input bitcode files>")); @@ -77,6 +95,9 @@ static cl::opt<bool> SetMergedModule( "set-merged-module", cl::init(false), cl::desc("Use the first input module as the merged module")); +static cl::opt<unsigned> Parallelism("j", cl::Prefix, cl::init(1), + cl::desc("Number of backend threads")); + namespace { struct ModuleInfo { std::vector<bool> CanBeHidden; @@ -85,6 +106,7 @@ struct ModuleInfo { static void handleDiagnostics(lto_codegen_diagnostic_severity_t Severity, const char *Msg, void *) { + errs() << "llvm-lto: "; switch (Severity) { case LTO_DS_NOTE: errs() << "note: "; @@ -102,18 +124,68 @@ static void handleDiagnostics(lto_codegen_diagnostic_severity_t Severity, errs() << Msg << "\n"; } +static std::string CurrentActivity; +static void diagnosticHandler(const DiagnosticInfo &DI) { + raw_ostream &OS = errs(); + OS << "llvm-lto: "; + switch (DI.getSeverity()) { + case DS_Error: + OS << "error"; + break; + case DS_Warning: + OS << "warning"; + break; + case DS_Remark: + OS << "remark"; + break; + case DS_Note: + OS << "note"; + break; + } + if (!CurrentActivity.empty()) + OS << ' ' << CurrentActivity; + OS << ": "; + + DiagnosticPrinterRawOStream DP(OS); + DI.print(DP); + OS << '\n'; + + if (DI.getSeverity() == DS_Error) + exit(1); +} + +static void diagnosticHandlerWithContenxt(const DiagnosticInfo &DI, + void *Context) { + diagnosticHandler(DI); +} + +static void error(const Twine &Msg) { + errs() << "llvm-lto: " << Msg << '\n'; + exit(1); +} + +static void error(std::error_code EC, const Twine &Prefix) { + if (EC) + error(Prefix + ": " + EC.message()); +} + +template <typename T> +static void error(const ErrorOr<T> &V, const Twine &Prefix) { + error(V.getError(), Prefix); +} + static std::unique_ptr<LTOModule> getLocalLTOModule(StringRef Path, std::unique_ptr<MemoryBuffer> &Buffer, - const TargetOptions &Options, std::string &Error) { + const TargetOptions &Options) { ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = MemoryBuffer::getFile(Path); - if (std::error_code EC = BufferOrErr.getError()) { - Error = EC.message(); - return nullptr; - } + error(BufferOrErr, "error loading file '" + Path + "'"); Buffer = std::move(BufferOrErr.get()); - return std::unique_ptr<LTOModule>(LTOModule::createInLocalContext( - Buffer->getBufferStart(), Buffer->getBufferSize(), Options, Error, Path)); + CurrentActivity = ("loading file '" + Path + "'").str(); + ErrorOr<std::unique_ptr<LTOModule>> Ret = LTOModule::createInLocalContext( + Buffer->getBufferStart(), Buffer->getBufferSize(), Options, Path); + CurrentActivity = ""; + return std::move(*Ret); } /// \brief List symbols in each IR file. @@ -122,24 +194,44 @@ getLocalLTOModule(StringRef Path, std::unique_ptr<MemoryBuffer> &Buffer, /// functionality that's exposed by the C API to list symbols. Moreover, this /// provides testing coverage for modules that have been created in their own /// contexts. -static int listSymbols(StringRef Command, const TargetOptions &Options) { +static void listSymbols(const TargetOptions &Options) { for (auto &Filename : InputFilenames) { - std::string Error; std::unique_ptr<MemoryBuffer> Buffer; std::unique_ptr<LTOModule> Module = - getLocalLTOModule(Filename, Buffer, Options, Error); - if (!Module) { - errs() << Command << ": error loading file '" << Filename - << "': " << Error << "\n"; - return 1; - } + getLocalLTOModule(Filename, Buffer, Options); // List the symbols. outs() << Filename << ":\n"; for (int I = 0, E = Module->getSymbolCount(); I != E; ++I) outs() << Module->getSymbolName(I) << "\n"; } - return 0; +} + +/// Create a combined index file from the input IR files and write it. +/// +/// This is meant to enable testing of ThinLTO combined index generation, +/// currently available via the gold plugin via -thinlto. +static void createCombinedFunctionIndex() { + FunctionInfoIndex CombinedIndex; + uint64_t NextModuleId = 0; + for (auto &Filename : InputFilenames) { + CurrentActivity = "loading file '" + Filename + "'"; + ErrorOr<std::unique_ptr<FunctionInfoIndex>> IndexOrErr = + llvm::getFunctionIndexForFile(Filename, diagnosticHandler); + std::unique_ptr<FunctionInfoIndex> Index = std::move(IndexOrErr.get()); + CurrentActivity = ""; + // Skip files without a function summary. + if (!Index) + continue; + CombinedIndex.mergeFrom(std::move(Index), ++NextModuleId); + } + std::error_code EC; + assert(!OutputFilename.empty()); + raw_fd_ostream OS(OutputFilename + ".thinlto.bc", EC, + sys::fs::OpenFlags::F_None); + error(EC, "error opening the file '" + OutputFilename + ".thinlto.bc'"); + WriteFunctionSummaryToFile(CombinedIndex, OS); + OS.close(); } int main(int argc, char **argv) { @@ -150,10 +242,8 @@ int main(int argc, char **argv) { llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. cl::ParseCommandLineOptions(argc, argv, "llvm LTO linker\n"); - if (OptLevel < '0' || OptLevel > '3') { - errs() << argv[0] << ": optimization level must be between 0 and 3\n"; - return 1; - } + if (OptLevel < '0' || OptLevel > '3') + error("optimization level must be between 0 and 3"); // Initialize the configured targets. InitializeAllTargets(); @@ -164,29 +254,27 @@ int main(int argc, char **argv) { // set up the TargetOptions for the machine TargetOptions Options = InitTargetOptionsFromCodeGenFlags(); - if (ListSymbolsOnly) - return listSymbols(argv[0], Options); + if (ListSymbolsOnly) { + listSymbols(Options); + return 0; + } + + if (ThinLTO) { + createCombinedFunctionIndex(); + return 0; + } unsigned BaseArg = 0; - LTOCodeGenerator CodeGen; + LLVMContext Context; + Context.setDiagnosticHandler(diagnosticHandlerWithContenxt, nullptr, true); + + LTOCodeGenerator CodeGen(Context); if (UseDiagnosticHandler) CodeGen.setDiagnosticHandler(handleDiagnostics, nullptr); - switch (RelocModel) { - case Reloc::Static: - CodeGen.setCodePICModel(LTO_CODEGEN_PIC_MODEL_STATIC); - break; - case Reloc::PIC_: - CodeGen.setCodePICModel(LTO_CODEGEN_PIC_MODEL_DYNAMIC); - break; - case Reloc::DynamicNoPIC: - CodeGen.setCodePICModel(LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC); - break; - default: - CodeGen.setCodePICModel(LTO_CODEGEN_PIC_MODEL_DEFAULT); - } + CodeGen.setCodePICModel(RelocModel); CodeGen.setDebugInfo(LTO_DEBUG_MODEL_DWARF); CodeGen.setTargetOptions(Options); @@ -198,38 +286,33 @@ int main(int argc, char **argv) { std::vector<std::string> KeptDSOSyms; for (unsigned i = BaseArg; i < InputFilenames.size(); ++i) { - std::string error; - std::unique_ptr<LTOModule> Module( - LTOModule::createFromFile(InputFilenames[i].c_str(), Options, error)); - if (!error.empty()) { - errs() << argv[0] << ": error loading file '" << InputFilenames[i] - << "': " << error << "\n"; - return 1; - } + CurrentActivity = "loading file '" + InputFilenames[i] + "'"; + ErrorOr<std::unique_ptr<LTOModule>> ModuleOrErr = + LTOModule::createFromFile(Context, InputFilenames[i].c_str(), Options); + std::unique_ptr<LTOModule> &Module = *ModuleOrErr; + CurrentActivity = ""; - LTOModule *LTOMod = Module.get(); + unsigned NumSyms = Module->getSymbolCount(); + for (unsigned I = 0; I < NumSyms; ++I) { + StringRef Name = Module->getSymbolName(I); + if (!DSOSymbolsSet.count(Name)) + continue; + lto_symbol_attributes Attrs = Module->getSymbolAttributes(I); + unsigned Scope = Attrs & LTO_SYMBOL_SCOPE_MASK; + if (Scope != LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN) + KeptDSOSyms.push_back(Name); + } // We use the first input module as the destination module when // SetMergedModule is true. if (SetMergedModule && i == BaseArg) { // Transfer ownership to the code generator. - CodeGen.setModule(Module.release()); + CodeGen.setModule(std::move(Module)); } else if (!CodeGen.addModule(Module.get())) { // Print a message here so that we know addModule() did not abort. errs() << argv[0] << ": error adding file '" << InputFilenames[i] << "'\n"; return 1; } - - unsigned NumSyms = LTOMod->getSymbolCount(); - for (unsigned I = 0; I < NumSyms; ++I) { - StringRef Name = LTOMod->getSymbolName(I); - if (!DSOSymbolsSet.count(Name)) - continue; - lto_symbol_attributes Attrs = LTOMod->getSymbolAttributes(I); - unsigned Scope = Attrs & LTO_SYMBOL_SCOPE_MASK; - if (Scope != LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN) - KeptDSOSyms.push_back(Name); - } } // Add all the exported symbols to the table of symbols to preserve. @@ -255,34 +338,68 @@ int main(int argc, char **argv) { if (!attrs.empty()) CodeGen.setAttr(attrs.c_str()); + if (FileType.getNumOccurrences()) + CodeGen.setFileType(FileType); + if (!OutputFilename.empty()) { - std::string ErrorInfo; - std::unique_ptr<MemoryBuffer> Code = CodeGen.compile( - DisableInline, DisableGVNLoadPRE, DisableLTOVectorization, ErrorInfo); - if (!Code) { - errs() << argv[0] - << ": error compiling the code: " << ErrorInfo << "\n"; + if (!CodeGen.optimize(DisableVerify, DisableInline, DisableGVNLoadPRE, + DisableLTOVectorization)) { + // Diagnostic messages should have been printed by the handler. + errs() << argv[0] << ": error optimizing the code\n"; return 1; } - std::error_code EC; - raw_fd_ostream FileStream(OutputFilename, EC, sys::fs::F_None); - if (EC) { - errs() << argv[0] << ": error opening the file '" << OutputFilename - << "': " << EC.message() << "\n"; + if (SaveModuleFile) { + std::string ModuleFilename = OutputFilename; + ModuleFilename += ".merged.bc"; + std::string ErrMsg; + + if (!CodeGen.writeMergedModules(ModuleFilename.c_str())) { + errs() << argv[0] << ": writing merged module failed.\n"; + return 1; + } + } + + std::list<tool_output_file> OSs; + std::vector<raw_pwrite_stream *> OSPtrs; + for (unsigned I = 0; I != Parallelism; ++I) { + std::string PartFilename = OutputFilename; + if (Parallelism != 1) + PartFilename += "." + utostr(I); + std::error_code EC; + OSs.emplace_back(PartFilename, EC, sys::fs::F_None); + if (EC) { + errs() << argv[0] << ": error opening the file '" << PartFilename + << "': " << EC.message() << "\n"; + return 1; + } + OSPtrs.push_back(&OSs.back().os()); + } + + if (!CodeGen.compileOptimized(OSPtrs)) { + // Diagnostic messages should have been printed by the handler. + errs() << argv[0] << ": error compiling the code\n"; return 1; } - FileStream.write(Code->getBufferStart(), Code->getBufferSize()); + for (tool_output_file &OS : OSs) + OS.keep(); } else { - std::string ErrorInfo; + if (Parallelism != 1) { + errs() << argv[0] << ": -j must be specified together with -o\n"; + return 1; + } + + if (SaveModuleFile) { + errs() << argv[0] << ": -save-merged-module must be specified with -o\n"; + return 1; + } + const char *OutputName = nullptr; - if (!CodeGen.compile_to_file(&OutputName, DisableInline, - DisableGVNLoadPRE, DisableLTOVectorization, - ErrorInfo)) { - errs() << argv[0] - << ": error compiling the code: " << ErrorInfo - << "\n"; + if (!CodeGen.compile_to_file(&OutputName, DisableVerify, DisableInline, + DisableGVNLoadPRE, DisableLTOVectorization)) { + // Diagnostic messages should have been printed by the handler. + errs() << argv[0] << ": error compiling the code\n"; return 1; } diff --git a/tools/llvm-mc-fuzzer/CMakeLists.txt b/tools/llvm-mc-fuzzer/CMakeLists.txt new file mode 100644 index 000000000000..0d6432b23dfa --- /dev/null +++ b/tools/llvm-mc-fuzzer/CMakeLists.txt @@ -0,0 +1,18 @@ +if( LLVM_USE_SANITIZE_COVERAGE ) + include_directories(BEFORE + ${CMAKE_CURRENT_SOURCE_DIR}/../../lib/Fuzzer) + + set(LLVM_LINK_COMPONENTS + AllTargetsDescs + AllTargetsDisassemblers + AllTargetsInfos + MC + MCDisassembler + Support + ) + add_llvm_tool(llvm-mc-fuzzer + llvm-mc-fuzzer.cpp) + target_link_libraries(llvm-mc-fuzzer + LLVMFuzzerNoMain + ) +endif() diff --git a/tools/llvm-mc-fuzzer/llvm-mc-fuzzer.cpp b/tools/llvm-mc-fuzzer/llvm-mc-fuzzer.cpp new file mode 100644 index 000000000000..3f80e4582ee1 --- /dev/null +++ b/tools/llvm-mc-fuzzer/llvm-mc-fuzzer.cpp @@ -0,0 +1,138 @@ +//===--- llvm-mc-fuzzer.cpp - Fuzzer for the MC layer ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// + +#include "llvm-c/Disassembler.h" +#include "llvm-c/Target.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/MC/SubtargetFeature.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/raw_ostream.h" +#include "FuzzerInterface.h" + +using namespace llvm; + +const unsigned AssemblyTextBufSize = 80; + +enum ActionType { + AC_Assemble, + AC_Disassemble +}; + +static cl::opt<ActionType> +Action(cl::desc("Action to perform:"), + cl::init(AC_Assemble), + cl::values(clEnumValN(AC_Assemble, "assemble", + "Assemble a .s file (default)"), + clEnumValN(AC_Disassemble, "disassemble", + "Disassemble strings of hex bytes"), + clEnumValEnd)); + +static cl::opt<std::string> + TripleName("triple", cl::desc("Target triple to assemble for, " + "see -version for available targets")); + +static cl::opt<std::string> + MCPU("mcpu", + cl::desc("Target a specific cpu type (-mcpu=help for details)"), + cl::value_desc("cpu-name"), cl::init("")); + +// This is useful for variable-length instruction sets. +static cl::opt<unsigned> InsnLimit( + "insn-limit", + cl::desc("Limit the number of instructions to process (0 for no limit)"), + cl::value_desc("count"), cl::init(0)); + +static cl::list<std::string> + MAttrs("mattr", cl::CommaSeparated, + cl::desc("Target specific attributes (-mattr=help for details)"), + cl::value_desc("a1,+a2,-a3,...")); +// The feature string derived from -mattr's values. +std::string FeaturesStr; + +static cl::list<std::string> + FuzzerArgv("fuzzer-args", cl::Positional, + cl::desc("Options to pass to the fuzzer"), cl::ZeroOrMore, + cl::PositionalEatsArgs); + +int DisassembleOneInput(const uint8_t *Data, size_t Size) { + char AssemblyText[AssemblyTextBufSize]; + + std::vector<uint8_t> DataCopy(Data, Data + Size); + + LLVMDisasmContextRef Ctx = LLVMCreateDisasmCPUFeatures( + TripleName.c_str(), MCPU.c_str(), FeaturesStr.c_str(), nullptr, 0, + nullptr, nullptr); + assert(Ctx); + uint8_t *p = DataCopy.data(); + unsigned Consumed; + unsigned InstructionsProcessed = 0; + do { + Consumed = LLVMDisasmInstruction(Ctx, p, Size, 0, AssemblyText, + AssemblyTextBufSize); + Size -= Consumed; + p += Consumed; + + InstructionsProcessed ++; + if (InsnLimit != 0 && InstructionsProcessed < InsnLimit) + break; + } while (Consumed != 0); + LLVMDisasmDispose(Ctx); + return 0; +} + +int main(int argc, char **argv) { + // The command line is unusual compared to other fuzzers due to the need to + // specify the target. Options like -triple, -mcpu, and -mattr work like + // their counterparts in llvm-mc, while -fuzzer-args collects options for the + // fuzzer itself. + // + // Examples: + // + // Fuzz the big-endian MIPS32R6 disassembler using 100,000 inputs of up to + // 4-bytes each and use the contents of ./corpus as the test corpus: + // llvm-mc-fuzzer -triple mips-linux-gnu -mcpu=mips32r6 -disassemble \ + // -fuzzer-args -max_len=4 -runs=100000 ./corpus + // + // Infinitely fuzz the little-endian MIPS64R2 disassembler with the MSA + // feature enabled using up to 64-byte inputs: + // llvm-mc-fuzzer -triple mipsel-linux-gnu -mcpu=mips64r2 -mattr=msa \ + // -disassemble -fuzzer-args ./corpus + // + // If your aim is to find instructions that are not tested, then it is + // advisable to constrain the maximum input size to a single instruction + // using -max_len as in the first example. This results in a test corpus of + // individual instructions that test unique paths. Without this constraint, + // there will be considerable redundancy in the corpus. + + LLVMInitializeAllTargetInfos(); + LLVMInitializeAllTargetMCs(); + LLVMInitializeAllDisassemblers(); + + cl::ParseCommandLineOptions(argc, argv); + + // Package up features to be passed to target/subtarget + // We have to pass it via a global since the callback doesn't + // permit any user data. + if (MAttrs.size()) { + SubtargetFeatures Features; + for (unsigned i = 0; i != MAttrs.size(); ++i) + Features.AddFeature(MAttrs[i]); + FeaturesStr = Features.getString(); + } + + if (Action == AC_Assemble) + errs() << "error: -assemble is not implemented\n"; + else if (Action == AC_Disassemble) + return fuzzer::FuzzerDriver(argc, argv, DisassembleOneInput); + + llvm_unreachable("Unknown action"); + return 1; +} diff --git a/tools/llvm-mc/llvm-mc.cpp b/tools/llvm-mc/llvm-mc.cpp index 6ecdb2eaa6d5..96e3f7c21a51 100644 --- a/tools/llvm-mc/llvm-mc.cpp +++ b/tools/llvm-mc/llvm-mc.cpp @@ -1,4 +1,4 @@ -//===-- llvm-mc.cpp - Machine Code Hacking Driver -------------------------===// +//===-- llvm-mc.cpp - Machine Code Hacking Driver ---------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -39,6 +39,7 @@ #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/ToolOutputFile.h" + using namespace llvm; static cl::opt<std::string> @@ -234,7 +235,7 @@ static void setDwarfDebugFlags(int argc, char **argv) { } static std::string DwarfDebugProducer; -static void setDwarfDebugProducer(void) { +static void setDwarfDebugProducer() { if(!getenv("DEBUG_PRODUCER")) return; DwarfDebugProducer += getenv("DEBUG_PRODUCER"); @@ -398,7 +399,7 @@ int main(int argc, char **argv) { ErrorOr<std::unique_ptr<MemoryBuffer>> BufferPtr = MemoryBuffer::getFileOrSTDIN(InputFilename); if (std::error_code EC = BufferPtr.getError()) { - errs() << ProgName << ": " << EC.message() << '\n'; + errs() << InputFilename << ": " << EC.message() << '\n'; return 1; } MemoryBuffer *Buffer = BufferPtr->get(); @@ -510,9 +511,10 @@ int main(int argc, char **argv) { MCCodeEmitter *CE = TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx); MCAsmBackend *MAB = TheTarget->createMCAsmBackend(*MRI, TripleName, MCPU); - Str.reset(TheTarget->createMCObjectStreamer(TheTriple, Ctx, *MAB, *OS, CE, - *STI, RelaxAll, - /*DWARFMustBeAtTheEnd*/ false)); + Str.reset(TheTarget->createMCObjectStreamer( + TheTriple, Ctx, *MAB, *OS, CE, *STI, MCOptions.MCRelaxAll, + MCOptions.MCIncrementalLinkerCompatible, + /*DWARFMustBeAtTheEnd*/ false)); if (NoExecStack) Str->InitSections(true); } diff --git a/tools/llvm-nm/CMakeLists.txt b/tools/llvm-nm/CMakeLists.txt index 20293bb88f05..22b7bd3e35e9 100644 --- a/tools/llvm-nm/CMakeLists.txt +++ b/tools/llvm-nm/CMakeLists.txt @@ -1,5 +1,7 @@ set(LLVM_LINK_COMPONENTS - ${LLVM_TARGETS_TO_BUILD} + AllTargetsAsmParsers + AllTargetsDescs + AllTargetsInfos Core Object Support diff --git a/tools/llvm-nm/llvm-nm.cpp b/tools/llvm-nm/llvm-nm.cpp index e7ee3124ed73..b812233034ee 100644 --- a/tools/llvm-nm/llvm-nm.cpp +++ b/tools/llvm-nm/llvm-nm.cpp @@ -20,6 +20,7 @@ #include "llvm/IR/GlobalAlias.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" #include "llvm/Object/Archive.h" #include "llvm/Object/COFF.h" #include "llvm/Object/ELFObjectFile.h" @@ -44,6 +45,7 @@ #include <cstring> #include <system_error> #include <vector> + using namespace llvm; using namespace object; @@ -64,27 +66,30 @@ cl::list<std::string> InputFilenames(cl::Positional, cl::desc("<input files>"), cl::opt<bool> UndefinedOnly("undefined-only", cl::desc("Show only undefined symbols")); cl::alias UndefinedOnly2("u", cl::desc("Alias for --undefined-only"), - cl::aliasopt(UndefinedOnly)); + cl::aliasopt(UndefinedOnly), cl::Grouping); cl::opt<bool> DynamicSyms("dynamic", cl::desc("Display the dynamic symbols instead " "of normal symbols.")); cl::alias DynamicSyms2("D", cl::desc("Alias for --dynamic"), - cl::aliasopt(DynamicSyms)); + cl::aliasopt(DynamicSyms), cl::Grouping); cl::opt<bool> DefinedOnly("defined-only", cl::desc("Show only defined symbols")); cl::alias DefinedOnly2("U", cl::desc("Alias for --defined-only"), - cl::aliasopt(DefinedOnly)); + cl::aliasopt(DefinedOnly), cl::Grouping); cl::opt<bool> ExternalOnly("extern-only", cl::desc("Show only external symbols")); cl::alias ExternalOnly2("g", cl::desc("Alias for --extern-only"), - cl::aliasopt(ExternalOnly)); + cl::aliasopt(ExternalOnly), cl::Grouping); -cl::opt<bool> BSDFormat("B", cl::desc("Alias for --format=bsd")); -cl::opt<bool> POSIXFormat("P", cl::desc("Alias for --format=posix")); -cl::opt<bool> DarwinFormat("m", cl::desc("Alias for --format=darwin")); +cl::opt<bool> BSDFormat("B", cl::desc("Alias for --format=bsd"), + cl::Grouping); +cl::opt<bool> POSIXFormat("P", cl::desc("Alias for --format=posix"), + cl::Grouping); +cl::opt<bool> DarwinFormat("m", cl::desc("Alias for --format=darwin"), + cl::Grouping); static cl::list<std::string> ArchFlags("arch", cl::desc("architecture(s) from a Mach-O file to dump"), @@ -96,32 +101,33 @@ cl::opt<bool> PrintFileName( cl::desc("Precede each symbol with the object file it came from")); cl::alias PrintFileNameA("A", cl::desc("Alias for --print-file-name"), - cl::aliasopt(PrintFileName)); + cl::aliasopt(PrintFileName), cl::Grouping); cl::alias PrintFileNameo("o", cl::desc("Alias for --print-file-name"), - cl::aliasopt(PrintFileName)); + cl::aliasopt(PrintFileName), cl::Grouping); cl::opt<bool> DebugSyms("debug-syms", cl::desc("Show all symbols, even debugger only")); cl::alias DebugSymsa("a", cl::desc("Alias for --debug-syms"), - cl::aliasopt(DebugSyms)); + cl::aliasopt(DebugSyms), cl::Grouping); cl::opt<bool> NumericSort("numeric-sort", cl::desc("Sort symbols by address")); cl::alias NumericSortn("n", cl::desc("Alias for --numeric-sort"), - cl::aliasopt(NumericSort)); + cl::aliasopt(NumericSort), cl::Grouping); cl::alias NumericSortv("v", cl::desc("Alias for --numeric-sort"), - cl::aliasopt(NumericSort)); + cl::aliasopt(NumericSort), cl::Grouping); cl::opt<bool> NoSort("no-sort", cl::desc("Show symbols in order encountered")); -cl::alias NoSortp("p", cl::desc("Alias for --no-sort"), cl::aliasopt(NoSort)); +cl::alias NoSortp("p", cl::desc("Alias for --no-sort"), cl::aliasopt(NoSort), + cl::Grouping); cl::opt<bool> ReverseSort("reverse-sort", cl::desc("Sort in reverse order")); cl::alias ReverseSortr("r", cl::desc("Alias for --reverse-sort"), - cl::aliasopt(ReverseSort)); + cl::aliasopt(ReverseSort), cl::Grouping); cl::opt<bool> PrintSize("print-size", cl::desc("Show symbol size instead of address")); cl::alias PrintSizeS("S", cl::desc("Alias for --print-size"), - cl::aliasopt(PrintSize)); + cl::aliasopt(PrintSize), cl::Grouping); cl::opt<bool> SizeSort("size-sort", cl::desc("Sort symbols by size")); @@ -130,12 +136,12 @@ cl::opt<bool> WithoutAliases("without-aliases", cl::Hidden, cl::opt<bool> ArchiveMap("print-armap", cl::desc("Print the archive map")); cl::alias ArchiveMaps("M", cl::desc("Alias for --print-armap"), - cl::aliasopt(ArchiveMap)); + cl::aliasopt(ArchiveMap), cl::Grouping); 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)); + cl::aliasopt(JustSymbolName), cl::Grouping); // FIXME: This option takes exactly two strings and should be allowed anywhere // on the command line. Such that "llvm-nm -s __TEXT __text foo.o" would work. @@ -147,7 +153,7 @@ cl::list<std::string> SegSect("s", cl::Positional, cl::ZeroOrMore, "and section name, Mach-O only")); cl::opt<bool> FormatMachOasHex("x", cl::desc("Print symbol entry in hex, " - "Mach-O only")); + "Mach-O only"), cl::Grouping); cl::opt<bool> NoLLVMBitcode("no-llvm-bc", cl::desc("Disable LLVM bitcode reader")); @@ -159,7 +165,7 @@ bool MultipleFiles = false; bool HadError = false; std::string ToolName; -} +} // anonymous namespace static void error(Twine Message, Twine Path = Twine()) { HadError = true; @@ -182,7 +188,7 @@ struct NMSymbol { StringRef Name; BasicSymbolRef Sym; }; -} +} // anonymous namespace static bool compareSymbolAddress(const NMSymbol &A, const NMSymbol &B) { bool ADefined = !(A.Sym.getFlags() & SymbolRef::SF_Undefined); @@ -202,8 +208,14 @@ static bool compareSymbolName(const NMSymbol &A, const NMSymbol &B) { } static char isSymbolList64Bit(SymbolicFile &Obj) { - if (isa<IRObjectFile>(Obj)) - return false; + if (isa<IRObjectFile>(Obj)) { + IRObjectFile *IRobj = dyn_cast<IRObjectFile>(&Obj); + Module &M = IRobj->getModule(); + if (M.getTargetTriple().empty()) + return false; + Triple T(M.getTargetTriple()); + return T.isArch64Bit(); + } if (isa<COFFObjectFile>(Obj)) return false; if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj)) @@ -215,54 +227,80 @@ static StringRef CurrentFilename; typedef std::vector<NMSymbol> SymbolListT; static SymbolListT SymbolList; +static char getSymbolNMTypeChar(IRObjectFile &Obj, basic_symbol_iterator I); + // darwinPrintSymbol() is used to print a symbol from a Mach-O file when the // 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) { +static void darwinPrintSymbol(SymbolicFile &Obj, SymbolListT::iterator I, + char *SymbolAddrStr, const char *printBlanks, + const char *printDashes, const char *printFormat) { MachO::mach_header H; MachO::mach_header_64 H_64; - uint32_t Filetype, Flags; - MachO::nlist_64 STE_64; - MachO::nlist STE; - uint8_t NType; - uint8_t NSect; - uint16_t NDesc; - uint32_t NStrx; - uint64_t NValue; - DataRefImpl SymDRI = I->Sym.getRawDataRefImpl(); - if (MachO->is64Bit()) { - H_64 = MachO->MachOObjectFile::getHeader64(); - Filetype = H_64.filetype; - Flags = H_64.flags; - STE_64 = MachO->getSymbol64TableEntry(SymDRI); - 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; + uint32_t Filetype = MachO::MH_OBJECT; + uint32_t Flags = 0; + uint8_t NType = 0; + uint8_t NSect = 0; + uint16_t NDesc = 0; + uint32_t NStrx = 0; + uint64_t NValue = 0; + MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj); + if (Obj.isIR()) { + uint32_t SymFlags = I->Sym.getFlags(); + if (SymFlags & SymbolRef::SF_Global) + NType |= MachO::N_EXT; + if (SymFlags & SymbolRef::SF_Hidden) + NType |= MachO::N_PEXT; + if (SymFlags & SymbolRef::SF_Undefined) + NType |= MachO::N_EXT | MachO::N_UNDF; + else { + // Here we have a symbol definition. So to fake out a section name we + // use 1, 2 and 3 for section numbers. See below where they are used to + // print out fake section names. + NType |= MachO::N_SECT; + if(SymFlags & SymbolRef::SF_Const) + NSect = 3; + else { + IRObjectFile *IRobj = dyn_cast<IRObjectFile>(&Obj); + char c = getSymbolNMTypeChar(*IRobj, I->Sym); + if (c == 't') + NSect = 1; + else + NSect = 2; + } + } + if (SymFlags & SymbolRef::SF_Weak) + NDesc |= MachO::N_WEAK_DEF; } else { - H = MachO->MachOObjectFile::getHeader(); - Filetype = H.filetype; - Flags = H.flags; - STE = MachO->getSymbolTableEntry(SymDRI); - NType = STE.n_type; - NSect = STE.n_sect; - NDesc = STE.n_desc; - NStrx = STE.n_strx; - NValue = STE.n_value; + DataRefImpl SymDRI = I->Sym.getRawDataRefImpl(); + if (MachO->is64Bit()) { + H_64 = MachO->MachOObjectFile::getHeader64(); + Filetype = H_64.filetype; + Flags = H_64.flags; + MachO::nlist_64 STE_64 = MachO->getSymbol64TableEntry(SymDRI); + 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(); + Filetype = H.filetype; + Flags = H.flags; + MachO::nlist STE = MachO->getSymbolTableEntry(SymDRI); + 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)); @@ -280,6 +318,8 @@ static void darwinPrintSymbol(MachOObjectFile *MachO, SymbolListT::iterator I, if (PrintAddress) { if ((NType & MachO::N_TYPE) == MachO::N_INDR) strcpy(SymbolAddrStr, printBlanks); + if (Obj.isIR() && (NType & MachO::N_TYPE) == MachO::N_TYPE) + strcpy(SymbolAddrStr, printDashes); outs() << SymbolAddrStr << ' '; } @@ -314,8 +354,20 @@ static void darwinPrintSymbol(MachOObjectFile *MachO, SymbolListT::iterator I, outs() << "(indirect) "; break; case MachO::N_SECT: { - section_iterator Sec = MachO->section_end(); - MachO->getSymbolSection(I->Sym.getRawDataRefImpl(), Sec); + if (Obj.isIR()) { + // For llvm bitcode files print out a fake section name using the values + // use 1, 2 and 3 for section numbers as set above. + if (NSect == 1) + outs() << "(LTO,CODE) "; + else if (NSect == 2) + outs() << "(LTO,DATA) "; + else if (NSect == 3) + outs() << "(LTO,RODATA) "; + else + outs() << "(?,?) "; + break; + } + section_iterator Sec = *MachO->getSymbolSection(I->Sym.getRawDataRefImpl()); DataRefImpl Ref = Sec->getRawDataRefImpl(); StringRef SectionName; MachO->getSectionName(Ref, SectionName); @@ -374,7 +426,8 @@ static void darwinPrintSymbol(MachOObjectFile *MachO, SymbolListT::iterator I, if ((NType & MachO::N_TYPE) == MachO::N_INDR) { outs() << I->Name << " (for "; StringRef IndirectName; - if (MachO->getIndirectName(I->Sym.getRawDataRefImpl(), IndirectName)) + if (!MachO || + MachO->getIndirectName(I->Sym.getRawDataRefImpl(), IndirectName)) outs() << "?)"; else outs() << IndirectName << ")"; @@ -392,7 +445,8 @@ static void darwinPrintSymbol(MachOObjectFile *MachO, SymbolListT::iterator I, outs() << " (dynamically looked up)"; else { StringRef LibraryName; - if (MachO->getLibraryShortNameByIndex(LibraryOrdinal - 1, LibraryName)) + if (!MachO || + MachO->getLibraryShortNameByIndex(LibraryOrdinal - 1, LibraryName)) outs() << " (from bad library ordinal " << LibraryOrdinal << ")"; else outs() << " (from " << LibraryName << ")"; @@ -440,13 +494,14 @@ static const struct DarwinStabName DarwinStabNames[] = { {MachO::N_ECOMM, "ECOMM"}, {MachO::N_ECOML, "ECOML"}, {MachO::N_LENG, "LENG"}, - {0, 0}}; + {0, nullptr}}; + static const char *getDarwinStabString(uint8_t NType) { for (unsigned i = 0; DarwinStabNames[i].Name; i++) { if (DarwinStabNames[i].NType == NType) return DarwinStabNames[i].Name; } - return 0; + return nullptr; } // darwinPrintStab() prints the n_sect, n_desc along with a symbolic name of @@ -511,12 +566,14 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName, } } - const char *printBlanks, *printFormat; + const char *printBlanks, *printDashes, *printFormat; if (isSymbolList64Bit(Obj)) { printBlanks = " "; + printDashes = "----------------"; printFormat = "%016" PRIx64; } else { printBlanks = " "; + printDashes = "--------"; printFormat = "%08" PRIx64; } @@ -528,6 +585,9 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName, continue; if (Undefined && DefinedOnly) continue; + bool Global = SymFlags & SymbolRef::SF_Global; + if (!Global && ExternalOnly) + continue; if (SizeSort && !PrintAddress) continue; if (PrintFileName) { @@ -537,7 +597,8 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName, outs() << ArchiveName << ":"; outs() << CurrentFilename << ": "; } - if (JustSymbolName || (UndefinedOnly && isa<MachOObjectFile>(Obj))) { + if ((JustSymbolName || (UndefinedOnly && isa<MachOObjectFile>(Obj) && + OutputFormat != darwin)) && OutputFormat != posix) { outs() << I->Name << "\n"; continue; } @@ -550,9 +611,13 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName, if (OutputFormat == sysv) strcpy(SymbolSizeStr, printBlanks); - if (I->TypeChar != 'U') - format(printFormat, I->Address) + if (I->TypeChar != 'U') { + if (Obj.isIR()) + strcpy(SymbolAddrStr, printDashes); + else + format(printFormat, I->Address) .print(SymbolAddrStr, sizeof(SymbolAddrStr)); + } format(printFormat, I->Size).print(SymbolSizeStr, sizeof(SymbolSizeStr)); // If OutputFormat is darwin or we are printing Mach-O symbols in hex and @@ -561,11 +626,15 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName, // printing Mach-O symbols in hex and not a Mach-O object fall back to // OutputFormat bsd (see below). MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj); - if ((OutputFormat == darwin || FormatMachOasHex) && MachO) { - darwinPrintSymbol(MachO, I, SymbolAddrStr, printBlanks); + if ((OutputFormat == darwin || FormatMachOasHex) && (MachO || Obj.isIR())) { + darwinPrintSymbol(Obj, I, SymbolAddrStr, printBlanks, printDashes, + printFormat); } else if (OutputFormat == posix) { - outs() << I->Name << " " << I->TypeChar << " " << SymbolAddrStr - << SymbolSizeStr << "\n"; + outs() << I->Name << " " << I->TypeChar << " "; + if (MachO) + outs() << I->Address << " " << "0" /* SymbolSizeStr */ << "\n"; + else + outs() << SymbolAddrStr << SymbolSizeStr << "\n"; } else if (OutputFormat == bsd || (OutputFormat == darwin && !MachO)) { if (PrintAddress) outs() << SymbolAddrStr << ' '; @@ -594,10 +663,11 @@ static char getSymbolNMTypeChar(ELFObjectFileBase &Obj, // OK, this is ELF elf_symbol_iterator SymI(I); - elf_section_iterator SecI = Obj.section_end(); - if (error(SymI->getSection(SecI))) + ErrorOr<elf_section_iterator> SecIOrErr = SymI->getSection(); + if (error(SecIOrErr.getError())) return '?'; + elf_section_iterator SecI = *SecIOrErr; if (SecI != Obj.section_end()) { switch (SecI->getType()) { case ELF::SHT_PROGBITS: @@ -651,9 +721,10 @@ static char getSymbolNMTypeChar(COFFObjectFile &Obj, symbol_iterator I) { uint32_t Characteristics = 0; if (!COFF::isReservedSectionNumber(Symb.getSectionNumber())) { - section_iterator SecI = Obj.section_end(); - if (error(SymI->getSection(SecI))) + ErrorOr<section_iterator> SecIOrErr = SymI->getSection(); + if (error(SecIOrErr.getError())) return '?'; + section_iterator SecI = *SecIOrErr; const coff_section *Section = Obj.getCOFFSection(*SecI); Characteristics = Section->Characteristics; } @@ -701,8 +772,7 @@ static char getSymbolNMTypeChar(MachOObjectFile &Obj, basic_symbol_iterator I) { case MachO::N_INDR: return 'i'; case MachO::N_SECT: { - section_iterator Sec = Obj.section_end(); - Obj.getSymbolSection(Symb, Sec); + section_iterator Sec = *Obj.getSymbolSection(Symb); DataRefImpl Ref = Sec->getRawDataRefImpl(); StringRef SectionName; Obj.getSectionName(Ref, SectionName); @@ -762,8 +832,14 @@ 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); + Triple Host(sys::getDefaultTargetTriple()); + if (Ret == 'd' && Host.isOSDarwin()) { + if(Symflags & SymbolRef::SF_Const) + Ret = 's'; + } + } else if (COFFObjectFile *COFF = dyn_cast<COFFObjectFile>(&Obj)) Ret = getSymbolNMTypeChar(*COFF, I); else if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj)) @@ -943,10 +1019,10 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { if (I != E) { outs() << "Archive map\n"; for (; I != E; ++I) { - ErrorOr<Archive::child_iterator> C = I->getMember(); + ErrorOr<Archive::Child> C = I->getMember(); if (error(C.getError())) return; - ErrorOr<StringRef> FileNameOrErr = C.get()->getName(); + ErrorOr<StringRef> FileNameOrErr = C->getName(); if (error(FileNameOrErr.getError())) return; StringRef SymName = I->getName(); @@ -958,7 +1034,10 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { for (Archive::child_iterator I = A->child_begin(), E = A->child_end(); I != E; ++I) { - ErrorOr<std::unique_ptr<Binary>> ChildOrErr = I->getAsBinary(&Context); + if (error(I->getError())) + return; + auto &C = I->get(); + ErrorOr<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(&Context); if (ChildOrErr.getError()) continue; if (SymbolicFile *O = dyn_cast<SymbolicFile>(&*ChildOrErr.get())) { @@ -1013,8 +1092,11 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { for (Archive::child_iterator AI = A->child_begin(), AE = A->child_end(); AI != AE; ++AI) { + if (error(AI->getError())) + return; + auto &C = AI->get(); ErrorOr<std::unique_ptr<Binary>> ChildOrErr = - AI->getAsBinary(&Context); + C.getAsBinary(&Context); if (ChildOrErr.getError()) continue; if (SymbolicFile *O = @@ -1067,8 +1149,11 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { for (Archive::child_iterator AI = A->child_begin(), AE = A->child_end(); AI != AE; ++AI) { + if (error(AI->getError())) + return; + auto &C = AI->get(); ErrorOr<std::unique_ptr<Binary>> ChildOrErr = - AI->getAsBinary(&Context); + C.getAsBinary(&Context); if (ChildOrErr.getError()) continue; if (SymbolicFile *O = @@ -1116,8 +1201,10 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { std::unique_ptr<Archive> &A = *AOrErr; for (Archive::child_iterator AI = A->child_begin(), AE = A->child_end(); AI != AE; ++AI) { - ErrorOr<std::unique_ptr<Binary>> ChildOrErr = - AI->getAsBinary(&Context); + if (error(AI->getError())) + return; + auto &C = AI->get(); + ErrorOr<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(&Context); if (ChildOrErr.getError()) continue; if (SymbolicFile *O = dyn_cast<SymbolicFile>(&*ChildOrErr.get())) { @@ -1150,7 +1237,6 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { return; } error("unrecognizable file type", Filename); - return; } int main(int argc, char **argv) { diff --git a/tools/llvm-objdump/CMakeLists.txt b/tools/llvm-objdump/CMakeLists.txt index 1f2721ab5452..d0dd4ac0182c 100644 --- a/tools/llvm-objdump/CMakeLists.txt +++ b/tools/llvm-objdump/CMakeLists.txt @@ -1,5 +1,8 @@ set(LLVM_LINK_COMPONENTS - ${LLVM_TARGETS_TO_BUILD} + AllTargetsAsmPrinters + AllTargetsDescs + AllTargetsDisassemblers + AllTargetsInfos CodeGen DebugInfoDWARF MC diff --git a/tools/llvm-objdump/COFFDump.cpp b/tools/llvm-objdump/COFFDump.cpp index 8b94a50ea28b..f286351614ac 100644 --- a/tools/llvm-objdump/COFFDump.cpp +++ b/tools/llvm-objdump/COFFDump.cpp @@ -151,7 +151,7 @@ static void printAllUnwindCodes(ArrayRef<UnwindCode> UCs) { << " remaining in buffer"; return ; } - printUnwindCode(ArrayRef<UnwindCode>(I, E)); + printUnwindCode(makeArrayRef(I, E)); I += UsedSlots; } } @@ -165,10 +165,10 @@ resolveSectionAndAddress(const COFFObjectFile *Obj, const SymbolRef &Sym, if (std::error_code EC = ResolvedAddrOrErr.getError()) return EC; ResolvedAddr = *ResolvedAddrOrErr; - section_iterator iter(Obj->section_begin()); - if (std::error_code EC = Sym.getSection(iter)) + ErrorOr<section_iterator> Iter = Sym.getSection(); + if (std::error_code EC = Iter.getError()) return EC; - ResolvedSection = Obj->getCOFFSection(*iter); + ResolvedSection = Obj->getCOFFSection(**Iter); return std::error_code(); } @@ -241,12 +241,10 @@ printSEHTable(const COFFObjectFile *Obj, uint32_t TableVA, int Count) { return; const pe32_header *PE32Header; - if (error(Obj->getPE32Header(PE32Header))) - return; + error(Obj->getPE32Header(PE32Header)); uint32_t ImageBase = PE32Header->ImageBase; uintptr_t IntPtr = 0; - if (error(Obj->getVaPtr(TableVA, IntPtr))) - return; + error(Obj->getVaPtr(TableVA, IntPtr)); const support::ulittle32_t *P = (const support::ulittle32_t *)IntPtr; outs() << "SEH Table:"; for (int I = 0; I < Count; ++I) @@ -257,8 +255,7 @@ printSEHTable(const COFFObjectFile *Obj, uint32_t TableVA, int Count) { static void printLoadConfiguration(const COFFObjectFile *Obj) { // Skip if it's not executable. const pe32_header *PE32Header; - if (error(Obj->getPE32Header(PE32Header))) - return; + error(Obj->getPE32Header(PE32Header)); if (!PE32Header) return; @@ -267,13 +264,11 @@ static void printLoadConfiguration(const COFFObjectFile *Obj) { return; const data_directory *DataDir; - if (error(Obj->getDataDirectory(COFF::LOAD_CONFIG_TABLE, DataDir))) - return; + error(Obj->getDataDirectory(COFF::LOAD_CONFIG_TABLE, DataDir)); uintptr_t IntPtr = 0; if (DataDir->RelativeVirtualAddress == 0) return; - if (error(Obj->getRvaPtr(DataDir->RelativeVirtualAddress, IntPtr))) - return; + error(Obj->getRvaPtr(DataDir->RelativeVirtualAddress, IntPtr)); auto *LoadConf = reinterpret_cast<const coff_load_configuration32 *>(IntPtr); outs() << "Load configuration:" @@ -381,8 +376,7 @@ static bool getPDataSection(const COFFObjectFile *Obj, const RuntimeFunction *&RFStart, int &NumRFs) { for (const SectionRef &Section : Obj->sections()) { StringRef Name; - if (error(Section.getName(Name))) - continue; + error(Section.getName(Name)); if (Name != ".pdata") continue; @@ -394,8 +388,7 @@ static bool getPDataSection(const COFFObjectFile *Obj, std::sort(Rels.begin(), Rels.end(), RelocAddressLess); ArrayRef<uint8_t> Contents; - if (error(Obj->getSectionContents(Pdata, Contents))) - continue; + error(Obj->getSectionContents(Pdata, Contents)); if (Contents.empty()) continue; @@ -440,7 +433,7 @@ static void printWin64EHUnwindInfo(const Win64EH::UnwindInfo *UI) { if (UI->NumCodes) outs() << " Unwind Codes:\n"; - printAllUnwindCodes(ArrayRef<UnwindCode>(&UI->UnwindCodes[0], UI->NumCodes)); + printAllUnwindCodes(makeArrayRef(&UI->UnwindCodes[0], UI->NumCodes)); outs() << "\n"; outs().flush(); @@ -499,11 +492,10 @@ static void printRuntimeFunctionRels(const COFFObjectFile *Obj, ArrayRef<uint8_t> XContents; uint64_t UnwindInfoOffset = 0; - if (error(getSectionContents( + error(getSectionContents( Obj, Rels, SectionOffset + /*offsetof(RuntimeFunction, UnwindInfoOffset)*/ 8, - XContents, UnwindInfoOffset))) - return; + XContents, UnwindInfoOffset)); if (XContents.empty()) return; @@ -550,3 +542,52 @@ void llvm::printCOFFFileHeader(const object::ObjectFile *Obj) { printImportTables(file); printExportTable(file); } + +void llvm::printCOFFSymbolTable(const COFFObjectFile *coff) { + for (unsigned SI = 0, SE = coff->getNumberOfSymbols(); SI != SE; ++SI) { + ErrorOr<COFFSymbolRef> Symbol = coff->getSymbol(SI); + StringRef Name; + error(Symbol.getError()); + error(coff->getSymbolName(*Symbol, Name)); + + outs() << "[" << format("%2d", SI) << "]" + << "(sec " << format("%2d", int(Symbol->getSectionNumber())) << ")" + << "(fl 0x00)" // Flag bits, which COFF doesn't have. + << "(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->getNumberOfAuxSymbols(); AI < AE; ++AI, ++SI) { + if (Symbol->isSectionDefinition()) { + const coff_aux_section_definition *asd; + error(coff->getAuxSymbol<coff_aux_section_definition>(SI + 1, asd)); + + int32_t AuxNumber = asd->getNumber(Symbol->isBigObj()); + + outs() << "AUX " + << format("scnlen 0x%x nreloc %d nlnno %d checksum 0x%x " + , unsigned(asd->Length) + , unsigned(asd->NumberOfRelocations) + , unsigned(asd->NumberOfLinenumbers) + , unsigned(asd->CheckSum)) + << format("assoc %d comdat %d\n" + , unsigned(AuxNumber) + , unsigned(asd->Selection)); + } else if (Symbol->isFileRecord()) { + const char *FileName; + error(coff->getAuxSymbol<char>(SI + 1, FileName)); + + StringRef Name(FileName, Symbol->getNumberOfAuxSymbols() * + coff->getSymbolTableEntrySize()); + outs() << "AUX " << Name.rtrim(StringRef("\0", 1)) << '\n'; + + SI = SI + Symbol->getNumberOfAuxSymbols(); + break; + } else { + outs() << "AUX Unknown\n"; + } + } + } +} diff --git a/tools/llvm-objdump/ELFDump.cpp b/tools/llvm-objdump/ELFDump.cpp index 2d0d7d7db0f3..7b44e3916c48 100644 --- a/tools/llvm-objdump/ELFDump.cpp +++ b/tools/llvm-objdump/ELFDump.cpp @@ -24,10 +24,8 @@ using namespace llvm::object; template <class ELFT> void printProgramHeaders(const ELFFile<ELFT> *o) { typedef ELFFile<ELFT> ELFO; outs() << "Program Header:\n"; - for (typename ELFO::Elf_Phdr_Iter pi = o->program_header_begin(), - pe = o->program_header_end(); - pi != pe; ++pi) { - switch (pi->p_type) { + for (const typename ELFO::Elf_Phdr &Phdr : o->program_headers()) { + switch (Phdr.p_type) { case ELF::PT_LOAD: outs() << " LOAD "; break; @@ -55,22 +53,16 @@ template <class ELFT> void printProgramHeaders(const ELFFile<ELFT> *o) { const char *Fmt = ELFT::Is64Bits ? "0x%016" PRIx64 " " : "0x%08" PRIx64 " "; - outs() << "off " - << format(Fmt, (uint64_t)pi->p_offset) - << "vaddr " - << format(Fmt, (uint64_t)pi->p_vaddr) - << "paddr " - << format(Fmt, (uint64_t)pi->p_paddr) - << format("align 2**%u\n", countTrailingZeros<uint64_t>(pi->p_align)) - << " filesz " - << format(Fmt, (uint64_t)pi->p_filesz) - << "memsz " - << format(Fmt, (uint64_t)pi->p_memsz) - << "flags " - << ((pi->p_flags & ELF::PF_R) ? "r" : "-") - << ((pi->p_flags & ELF::PF_W) ? "w" : "-") - << ((pi->p_flags & ELF::PF_X) ? "x" : "-") - << "\n"; + outs() << "off " << format(Fmt, (uint64_t)Phdr.p_offset) << "vaddr " + << format(Fmt, (uint64_t)Phdr.p_vaddr) << "paddr " + << format(Fmt, (uint64_t)Phdr.p_paddr) + << format("align 2**%u\n", + countTrailingZeros<uint64_t>(Phdr.p_align)) + << " filesz " << format(Fmt, (uint64_t)Phdr.p_filesz) + << "memsz " << format(Fmt, (uint64_t)Phdr.p_memsz) << "flags " + << ((Phdr.p_flags & ELF::PF_R) ? "r" : "-") + << ((Phdr.p_flags & ELF::PF_W) ? "w" : "-") + << ((Phdr.p_flags & ELF::PF_X) ? "x" : "-") << "\n"; } outs() << "\n"; } diff --git a/tools/llvm-objdump/MachODump.cpp b/tools/llvm-objdump/MachODump.cpp index 04c72f4856c8..a2f3bc8e8a7c 100644 --- a/tools/llvm-objdump/MachODump.cpp +++ b/tools/llvm-objdump/MachODump.cpp @@ -13,6 +13,7 @@ #include "llvm-objdump.h" #include "llvm-c/Disassembler.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Triple.h" @@ -97,11 +98,6 @@ cl::opt<bool> cl::desc("Print the linker optimization hints for " "Mach-O objects (requires -macho)")); -cl::list<std::string> - llvm::DumpSections("section", - cl::desc("Prints the specified segment,section for " - "Mach-O objects (requires -macho)")); - cl::opt<bool> llvm::InfoPlist("info-plist", cl::desc("Print the info plist section as strings for " @@ -138,6 +134,7 @@ static cl::opt<bool> NoSymbolicOperands( static cl::list<std::string> ArchFlags("arch", cl::desc("architecture(s) from a Mach-O file to dump"), cl::ZeroOrMore); + bool ArchAll = false; static std::string ThumbTripleName; @@ -210,19 +207,19 @@ static uint64_t DumpDataInCode(const uint8_t *bytes, uint64_t Length, case MachO::DICE_KIND_DATA: if (Length >= 4) { if (!NoShowRawInsn) - dumpBytes(ArrayRef<uint8_t>(bytes, 4), outs()); + dumpBytes(makeArrayRef(bytes, 4), outs()); Value = bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0]; outs() << "\t.long " << Value; Size = 4; } else if (Length >= 2) { if (!NoShowRawInsn) - dumpBytes(ArrayRef<uint8_t>(bytes, 2), outs()); + dumpBytes(makeArrayRef(bytes, 2), outs()); Value = bytes[1] << 8 | bytes[0]; outs() << "\t.short " << Value; Size = 2; } else { if (!NoShowRawInsn) - dumpBytes(ArrayRef<uint8_t>(bytes, 2), outs()); + dumpBytes(makeArrayRef(bytes, 2), outs()); Value = bytes[0]; outs() << "\t.byte " << Value; Size = 1; @@ -234,14 +231,14 @@ static uint64_t DumpDataInCode(const uint8_t *bytes, uint64_t Length, break; case MachO::DICE_KIND_JUMP_TABLE8: if (!NoShowRawInsn) - dumpBytes(ArrayRef<uint8_t>(bytes, 1), outs()); + dumpBytes(makeArrayRef(bytes, 1), outs()); Value = bytes[0]; outs() << "\t.byte " << format("%3u", Value) << "\t@ KIND_JUMP_TABLE8\n"; Size = 1; break; case MachO::DICE_KIND_JUMP_TABLE16: if (!NoShowRawInsn) - dumpBytes(ArrayRef<uint8_t>(bytes, 2), outs()); + dumpBytes(makeArrayRef(bytes, 2), outs()); Value = bytes[1] << 8 | bytes[0]; outs() << "\t.short " << format("%5u", Value & 0xffff) << "\t@ KIND_JUMP_TABLE16\n"; @@ -250,7 +247,7 @@ static uint64_t DumpDataInCode(const uint8_t *bytes, uint64_t Length, case MachO::DICE_KIND_JUMP_TABLE32: case MachO::DICE_KIND_ABS_JUMP_TABLE32: if (!NoShowRawInsn) - dumpBytes(ArrayRef<uint8_t>(bytes, 4), outs()); + dumpBytes(makeArrayRef(bytes, 4), outs()); Value = bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0]; outs() << "\t.long " << Value; if (Kind == MachO::DICE_KIND_JUMP_TABLE32) @@ -670,13 +667,9 @@ static void DumpLiteral8(MachOObjectFile *O, uint32_t l0, uint32_t l1, double d) { outs() << format("0x%08" PRIx32, l0) << " " << format("0x%08" PRIx32, l1); uint32_t Hi, Lo; - if (O->isLittleEndian()) { - Hi = l1; - Lo = l0; - } else { - Hi = l0; - Lo = l1; - } + Hi = (O->isLittleEndian()) ? l1 : l0; + Lo = (O->isLittleEndian()) ? l0 : l1; + // Hi is the high word, so this is equivalent to if(isfinite(d)) if ((Hi & 0x7ff00000) != 0x7ff00000) outs() << format(" (%.16e)\n", d); @@ -921,10 +914,7 @@ static void DumpInitTermPointerSection(MachOObjectFile *O, const char *sect, SymbolAddressMap *AddrMap, bool verbose) { uint32_t stride; - if (O->is64Bit()) - stride = sizeof(uint64_t); - else - stride = sizeof(uint32_t); + stride = (O->is64Bit()) ? sizeof(uint64_t) : sizeof(uint32_t); for (uint32_t i = 0; i < sect_size; i += stride) { const char *SymbolName = nullptr; if (O->is64Bit()) { @@ -1006,8 +996,8 @@ static void DumpSectionContents(StringRef Filename, MachOObjectFile *O, if (verbose) CreateSymbolAddressMap(O, &AddrMap); - for (unsigned i = 0; i < DumpSections.size(); ++i) { - StringRef DumpSection = DumpSections[i]; + for (unsigned i = 0; i < FilterSections.size(); ++i) { + StringRef DumpSection = FilterSections[i]; std::pair<StringRef, StringRef> DumpSegSectName; DumpSegSectName = DumpSection.split(','); StringRef DumpSegName, DumpSectName; @@ -1171,7 +1161,7 @@ static void ProcessMachO(StringRef Filename, MachOObjectFile *MachOOF, // UniversalHeaders or ArchiveHeaders. if (Disassemble || PrivateHeaders || ExportsTrie || Rebase || Bind || LazyBind || WeakBind || IndirectSymbols || DataInCode || LinkOptHints || - DylibsUsed || DylibId || ObjcMetaData || (DumpSections.size() != 0)) { + DylibsUsed || DylibId || ObjcMetaData || (FilterSections.size() != 0)) { outs() << Filename; if (!ArchiveMemberName.empty()) outs() << '(' << ArchiveMemberName << ')'; @@ -1194,7 +1184,7 @@ static void ProcessMachO(StringRef Filename, MachOObjectFile *MachOOF, PrintSectionHeaders(MachOOF); if (SectionContents) PrintSectionContents(MachOOF); - if (DumpSections.size() != 0) + if (FilterSections.size() != 0) DumpSectionContents(Filename, MachOOF, !NonVerbose); if (InfoPlist) DumpInfoPlistSectionContents(Filename, MachOOF); @@ -1395,7 +1385,7 @@ static void printMachOUniversalHeaders(const object::MachOUniversalBinary *UB, } } -static void printArchiveChild(Archive::Child &C, bool verbose, +static void printArchiveChild(const Archive::Child &C, bool verbose, bool print_offset) { if (print_offset) outs() << C.getChildOffset() << "\t"; @@ -1404,42 +1394,15 @@ static void printArchiveChild(Archive::Child &C, bool verbose, // FIXME: this first dash, "-", is for (Mode & S_IFMT) == S_IFREG. // But there is nothing in sys::fs::perms for S_IFMT or S_IFREG. outs() << "-"; - if (Mode & sys::fs::owner_read) - outs() << "r"; - else - outs() << "-"; - if (Mode & sys::fs::owner_write) - outs() << "w"; - else - outs() << "-"; - if (Mode & sys::fs::owner_exe) - outs() << "x"; - else - outs() << "-"; - if (Mode & sys::fs::group_read) - outs() << "r"; - else - outs() << "-"; - if (Mode & sys::fs::group_write) - outs() << "w"; - else - outs() << "-"; - if (Mode & sys::fs::group_exe) - outs() << "x"; - else - outs() << "-"; - if (Mode & sys::fs::others_read) - outs() << "r"; - else - outs() << "-"; - if (Mode & sys::fs::others_write) - outs() << "w"; - else - outs() << "-"; - if (Mode & sys::fs::others_exe) - outs() << "x"; - else - outs() << "-"; + outs() << ((Mode & sys::fs::owner_read) ? "r" : "-"); + outs() << ((Mode & sys::fs::owner_write) ? "w" : "-"); + outs() << ((Mode & sys::fs::owner_exe) ? "x" : "-"); + outs() << ((Mode & sys::fs::group_read) ? "r" : "-"); + outs() << ((Mode & sys::fs::group_write) ? "w" : "-"); + outs() << ((Mode & sys::fs::group_exe) ? "x" : "-"); + outs() << ((Mode & sys::fs::others_read) ? "r" : "-"); + outs() << ((Mode & sys::fs::others_write) ? "w" : "-"); + outs() << ((Mode & sys::fs::others_exe) ? "x" : "-"); } else { outs() << format("0%o ", Mode); } @@ -1448,8 +1411,10 @@ static void printArchiveChild(Archive::Child &C, bool verbose, outs() << format("%3d/", UID); unsigned GID = C.getGID(); outs() << format("%-3d ", GID); - uint64_t Size = C.getRawSize(); - outs() << format("%5" PRId64, Size) << " "; + ErrorOr<uint64_t> Size = C.getRawSize(); + if (std::error_code EC = Size.getError()) + report_fatal_error(EC.message()); + outs() << format("%5" PRId64, Size.get()) << " "; StringRef RawLastModified = C.getRawLastModified(); if (verbose) { @@ -1483,14 +1448,11 @@ static void printArchiveChild(Archive::Child &C, bool verbose, } static void printArchiveHeaders(Archive *A, bool verbose, bool print_offset) { - if (A->hasSymbolTable()) { - Archive::child_iterator S = A->getSymbolTableChild(); - Archive::Child C = *S; - printArchiveChild(C, verbose, print_offset); - } - for (Archive::child_iterator I = A->child_begin(), E = A->child_end(); I != E; - ++I) { - Archive::Child C = *I; + for (Archive::child_iterator I = A->child_begin(false), E = A->child_end(); + I != E; ++I) { + if (std::error_code EC = I->getError()) + report_fatal_error(EC.message()); + const Archive::Child &C = **I; printArchiveChild(C, verbose, print_offset); } } @@ -1527,7 +1489,10 @@ void llvm::ParseInputMachO(StringRef Filename) { printArchiveHeaders(A, !NonVerbose, ArchiveMemberOffsets); for (Archive::child_iterator I = A->child_begin(), E = A->child_end(); I != E; ++I) { - ErrorOr<std::unique_ptr<Binary>> ChildOrErr = I->getAsBinary(); + if (std::error_code EC = I->getError()) + report_error(Filename, EC); + auto &C = I->get(); + ErrorOr<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); if (ChildOrErr.getError()) continue; if (MachOObjectFile *O = dyn_cast<MachOObjectFile>(&*ChildOrErr.get())) { @@ -1575,7 +1540,10 @@ void llvm::ParseInputMachO(StringRef Filename) { for (Archive::child_iterator AI = A->child_begin(), AE = A->child_end(); AI != AE; ++AI) { - ErrorOr<std::unique_ptr<Binary>> ChildOrErr = AI->getAsBinary(); + if (std::error_code EC = AI->getError()) + report_error(Filename, EC); + auto &C = AI->get(); + ErrorOr<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); if (ChildOrErr.getError()) continue; if (MachOObjectFile *O = @@ -1617,7 +1585,10 @@ void llvm::ParseInputMachO(StringRef Filename) { for (Archive::child_iterator AI = A->child_begin(), AE = A->child_end(); AI != AE; ++AI) { - ErrorOr<std::unique_ptr<Binary>> ChildOrErr = AI->getAsBinary(); + if (std::error_code EC = AI->getError()) + report_error(Filename, EC); + auto &C = AI->get(); + ErrorOr<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); if (ChildOrErr.getError()) continue; if (MachOObjectFile *O = @@ -1653,7 +1624,10 @@ void llvm::ParseInputMachO(StringRef Filename) { printArchiveHeaders(A.get(), !NonVerbose, ArchiveMemberOffsets); for (Archive::child_iterator AI = A->child_begin(), AE = A->child_end(); AI != AE; ++AI) { - ErrorOr<std::unique_ptr<Binary>> ChildOrErr = AI->getAsBinary(); + if (std::error_code EC = AI->getError()) + report_error(Filename, EC); + auto &C = AI->get(); + ErrorOr<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); if (ChildOrErr.getError()) continue; if (MachOObjectFile *O = @@ -1676,8 +1650,7 @@ void llvm::ParseInputMachO(StringRef Filename) { errs() << "llvm-objdump: '" << Filename << "': " << "Object is not a Mach-O file type.\n"; } else - errs() << "llvm-objdump: '" << Filename << "': " - << "Unrecognized file type.\n"; + report_error(Filename, object_error::invalid_file_type); } typedef std::pair<uint64_t, const char *> BindInfoEntry; @@ -1698,6 +1671,7 @@ struct DisassembleInfo { uint64_t adrp_addr; uint32_t adrp_inst; BindTable *bindtable; + uint32_t depth; }; // SymbolizerGetOpInfo() is the operand information call back function. @@ -1735,8 +1709,15 @@ static int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset, 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. + if (info->O->getHeader().filetype != MachO::MH_OBJECT) { + // TODO: + // 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; + } + // In MH_OBJECT filetypes 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; @@ -1806,17 +1787,20 @@ static int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset, 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; } 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. + if (info->O->getHeader().filetype != MachO::MH_OBJECT) { + // TODO: + // 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; + } + // In MH_OBJECT filetypes 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; @@ -1874,17 +1858,20 @@ static int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset, 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; } 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. + if (info->O->getHeader().filetype != MachO::MH_OBJECT) { + // TODO: + // 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; + } + // In MH_OBJECT filetypes 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; DataRefImpl Rel; @@ -2016,8 +2003,15 @@ static int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset, 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. + if (info->O->getHeader().filetype != MachO::MH_OBJECT) { + // TODO: + // 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; + } + // In MH_OBJECT filetypes 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; auto Reloc = @@ -2370,6 +2364,8 @@ static const char *get_pointer_64(uint64_t Address, uint32_t &offset, for (unsigned SectIdx = 0; SectIdx != info->Sections->size(); SectIdx++) { uint64_t SectAddress = ((*(info->Sections))[SectIdx]).getAddress(); uint64_t SectSize = ((*(info->Sections))[SectIdx]).getSize(); + if (SectSize == 0) + continue; if (objc_only) { StringRef SectName; ((*(info->Sections))[SectIdx]).getName(SectName); @@ -3267,6 +3263,8 @@ walk_pointer_list_32(const char *listname, const SectionRef S, } static void print_layout_map(const char *layout_map, uint32_t left) { + if (layout_map == nullptr) + return; outs() << " layout map: "; do { outs() << format("0x%02" PRIx32, (*layout_map) & 0xff) << " "; @@ -3330,8 +3328,8 @@ static void print_method_list64_t(uint64_t p, struct DisassembleInfo *info, return; memset(&m, '\0', sizeof(struct method64_t)); if (left < sizeof(struct method64_t)) { - memcpy(&ml, r, left); - outs() << indent << " (method_t entends past the end of the section)\n"; + memcpy(&m, r, left); + outs() << indent << " (method_t extends past the end of the section)\n"; } else memcpy(&m, r, sizeof(struct method64_t)); if (info->O->isLittleEndian() != sys::IsLittleEndianHost) @@ -4222,7 +4220,7 @@ static void print_objc_property_list32(uint32_t p, } } -static void print_class_ro64_t(uint64_t p, struct DisassembleInfo *info, +static bool print_class_ro64_t(uint64_t p, struct DisassembleInfo *info, bool &is_meta_class) { struct class_ro64_t cro; const char *r; @@ -4233,7 +4231,7 @@ static void print_class_ro64_t(uint64_t p, struct DisassembleInfo *info, r = get_pointer_64(p, offset, left, S, info); if (r == nullptr || left < sizeof(struct class_ro64_t)) - return; + return false; memset(&cro, '\0', sizeof(struct class_ro64_t)); if (left < sizeof(struct class_ro64_t)) { memcpy(&cro, r, left); @@ -4357,10 +4355,11 @@ static void print_class_ro64_t(uint64_t p, struct DisassembleInfo *info, if (cro.baseProperties + n_value != 0) print_objc_property_list64(cro.baseProperties + n_value, info); - is_meta_class = (cro.flags & RO_META) ? true : false; + is_meta_class = (cro.flags & RO_META) != 0; + return true; } -static void print_class_ro32_t(uint32_t p, struct DisassembleInfo *info, +static bool print_class_ro32_t(uint32_t p, struct DisassembleInfo *info, bool &is_meta_class) { struct class_ro32_t cro; const char *r; @@ -4370,7 +4369,7 @@ static void print_class_ro32_t(uint32_t p, struct DisassembleInfo *info, r = get_pointer_32(p, offset, left, S, info); if (r == nullptr) - return; + return false; memset(&cro, '\0', sizeof(struct class_ro32_t)); if (left < sizeof(struct class_ro32_t)) { memcpy(&cro, r, left); @@ -4420,7 +4419,8 @@ static void print_class_ro32_t(uint32_t p, struct DisassembleInfo *info, << format("0x%" PRIx32, cro.baseProperties) << "\n"; if (cro.baseProperties != 0) print_objc_property_list32(cro.baseProperties, info); - is_meta_class = (cro.flags & RO_META) ? true : false; + is_meta_class = (cro.flags & RO_META) != 0; + return true; } static void print_class64_t(uint64_t p, struct DisassembleInfo *info) { @@ -4490,11 +4490,16 @@ static void print_class64_t(uint64_t p, struct DisassembleInfo *info) { outs() << " Swift class"; outs() << "\n"; bool is_meta_class; - print_class_ro64_t((c.data + n_value) & ~0x7, info, is_meta_class); + if (!print_class_ro64_t((c.data + n_value) & ~0x7, info, is_meta_class)) + return; - if (is_meta_class == false) { - outs() << "Meta Class\n"; - print_class64_t(c.isa + isa_n_value, info); + if (!is_meta_class && + c.isa + isa_n_value != p && + c.isa + isa_n_value != 0 && + info->depth < 100) { + info->depth++; + outs() << "Meta Class\n"; + print_class64_t(c.isa + isa_n_value, info); } } @@ -4555,9 +4560,10 @@ static void print_class32_t(uint32_t p, struct DisassembleInfo *info) { outs() << " Swift class"; outs() << "\n"; bool is_meta_class; - print_class_ro32_t(c.data & ~0x3, info, is_meta_class); + if (!print_class_ro32_t(c.data & ~0x3, info, is_meta_class)) + return; - if (is_meta_class == false) { + if (!is_meta_class) { outs() << "Meta Class\n"; print_class32_t(c.isa, info); } @@ -4865,7 +4871,7 @@ static void print_category32_t(uint32_t p, struct DisassembleInfo *info) { outs() << " name " << format("0x%" PRIx32, c.name); name = get_symbol_32(offset + offsetof(struct category32_t, name), S, info, c.name); - if (name != NULL) + if (name) outs() << " " << name; outs() << "\n"; @@ -5006,6 +5012,9 @@ static void print_image_info64(SectionRef S, struct DisassembleInfo *info) { struct objc_image_info64 o; const char *r; + if (S == SectionRef()) + return; + StringRef SectName; S.getName(SectName); DataRefImpl Ref = S.getRawDataRefImpl(); @@ -5142,75 +5151,48 @@ static void printObjc2_64bit_MetaData(MachOObjectFile *O, bool verbose) { info.adrp_addr = 0; info.adrp_inst = 0; - const SectionRef CL = get_section(O, "__OBJC2", "__class_list"); - if (CL != SectionRef()) { - info.S = CL; - walk_pointer_list_64("class", CL, O, &info, print_class64_t); - } else { - const SectionRef CL = get_section(O, "__DATA", "__objc_classlist"); - info.S = CL; - walk_pointer_list_64("class", CL, O, &info, print_class64_t); - } + info.depth = 0; + SectionRef CL = get_section(O, "__OBJC2", "__class_list"); + if (CL == SectionRef()) + CL = get_section(O, "__DATA", "__objc_classlist"); + info.S = CL; + walk_pointer_list_64("class", CL, O, &info, print_class64_t); - const SectionRef CR = get_section(O, "__OBJC2", "__class_refs"); - if (CR != SectionRef()) { - info.S = CR; - walk_pointer_list_64("class refs", CR, O, &info, nullptr); - } else { - const SectionRef CR = get_section(O, "__DATA", "__objc_classrefs"); - info.S = CR; - walk_pointer_list_64("class refs", CR, O, &info, nullptr); - } + SectionRef CR = get_section(O, "__OBJC2", "__class_refs"); + if (CR == SectionRef()) + CR = get_section(O, "__DATA", "__objc_classrefs"); + info.S = CR; + walk_pointer_list_64("class refs", CR, O, &info, nullptr); - const SectionRef SR = get_section(O, "__OBJC2", "__super_refs"); - if (SR != SectionRef()) { - info.S = SR; - walk_pointer_list_64("super refs", SR, O, &info, nullptr); - } else { - const SectionRef SR = get_section(O, "__DATA", "__objc_superrefs"); - info.S = SR; - walk_pointer_list_64("super refs", SR, O, &info, nullptr); - } + SectionRef SR = get_section(O, "__OBJC2", "__super_refs"); + if (SR == SectionRef()) + SR = get_section(O, "__DATA", "__objc_superrefs"); + info.S = SR; + walk_pointer_list_64("super refs", SR, O, &info, nullptr); - const SectionRef CA = get_section(O, "__OBJC2", "__category_list"); - if (CA != SectionRef()) { - info.S = CA; - walk_pointer_list_64("category", CA, O, &info, print_category64_t); - } else { - const SectionRef CA = get_section(O, "__DATA", "__objc_catlist"); - info.S = CA; - walk_pointer_list_64("category", CA, O, &info, print_category64_t); - } + SectionRef CA = get_section(O, "__OBJC2", "__category_list"); + if (CA == SectionRef()) + CA = get_section(O, "__DATA", "__objc_catlist"); + info.S = CA; + walk_pointer_list_64("category", CA, O, &info, print_category64_t); - const SectionRef PL = get_section(O, "__OBJC2", "__protocol_list"); - if (PL != SectionRef()) { - info.S = PL; - walk_pointer_list_64("protocol", PL, O, &info, nullptr); - } else { - const SectionRef PL = get_section(O, "__DATA", "__objc_protolist"); - info.S = PL; - walk_pointer_list_64("protocol", PL, O, &info, nullptr); - } + SectionRef PL = get_section(O, "__OBJC2", "__protocol_list"); + if (PL == SectionRef()) + PL = get_section(O, "__DATA", "__objc_protolist"); + info.S = PL; + walk_pointer_list_64("protocol", PL, O, &info, nullptr); - const SectionRef MR = get_section(O, "__OBJC2", "__message_refs"); - if (MR != SectionRef()) { - info.S = MR; - print_message_refs64(MR, &info); - } else { - const SectionRef MR = get_section(O, "__DATA", "__objc_msgrefs"); - info.S = MR; - print_message_refs64(MR, &info); - } + SectionRef MR = get_section(O, "__OBJC2", "__message_refs"); + if (MR == SectionRef()) + MR = get_section(O, "__DATA", "__objc_msgrefs"); + info.S = MR; + print_message_refs64(MR, &info); - const SectionRef II = get_section(O, "__OBJC2", "__image_info"); - if (II != SectionRef()) { - info.S = II; - print_image_info64(II, &info); - } else { - const SectionRef II = get_section(O, "__DATA", "__objc_imageinfo"); - info.S = II; - print_image_info64(II, &info); - } + SectionRef II = get_section(O, "__OBJC2", "__image_info"); + if (II == SectionRef()) + II = get_section(O, "__DATA", "__objc_imageinfo"); + info.S = II; + print_image_info64(II, &info); if (info.bindtable != nullptr) delete info.bindtable; @@ -5559,7 +5541,7 @@ static void printObjcMetaData(MachOObjectFile *O, bool verbose) { // binary for the iOS simulator which is the second Objective-C // ABI. In that case printObjc1_32bit_MetaData() will determine that // and return false. - if (printObjc1_32bit_MetaData(O, verbose) == false) + if (!printObjc1_32bit_MetaData(O, verbose)) printObjc2_32bit_MetaData(O, verbose); } } @@ -5588,36 +5570,38 @@ static const char *GuessLiteralPointer(uint64_t ReferenceValue, 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(); - 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; + if (info->O->getHeader().filetype == MachO::MH_OBJECT) { + 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(); + 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; } - 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) { - ReferenceValue = Symbol.getValue(); + // 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) { + ReferenceValue = Symbol.getValue(); + } } } } @@ -5872,7 +5856,6 @@ static void emitComments(raw_svector_ostream &CommentStream, 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(); @@ -5893,7 +5876,6 @@ static void emitComments(raw_svector_ostream &CommentStream, // Tell the comment stream that the vector changed underneath it. CommentsToEmit.clear(); - CommentStream.resync(); } static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, @@ -6065,7 +6047,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, diContext.reset(new DWARFContextInMemory(*DbgObj)); } - if (DumpSections.size() == 0) + if (FilterSections.size() == 0) outs() << "(" << DisSegName << "," << DisSectName << ") section\n"; for (unsigned SectIdx = 0; SectIdx != Sections.size(); SectIdx++) { @@ -6087,19 +6069,6 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, bool symbolTableWorked = false; - // Parse relocations. - std::vector<std::pair<uint64_t, SymbolRef>> Relocs; - for (const RelocationRef &Reloc : Sections[SectIdx].relocations()) { - uint64_t RelocOffset = Reloc.getOffset(); - uint64_t SectionAddress = Sections[SectIdx].getAddress(); - RelocOffset -= SectionAddress; - - symbol_iterator RelocSym = Reloc.getSymbol(); - - Relocs.push_back(std::make_pair(RelocOffset, *RelocSym)); - } - array_pod_sort(Relocs.begin(), Relocs.end()); - // Create a map of symbol addresses to symbol names for use by // the SymbolizerSymbolLookUp() routine. SymbolAddressMap AddrMap; @@ -6157,7 +6126,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, StringRef SymName = *SymNameOrErr; SymbolRef::Type ST = Symbols[SymIdx].getType(); - if (ST != SymbolRef::ST_Function) + if (ST != SymbolRef::ST_Function && ST != SymbolRef::ST_Data) continue; // Make sure the symbol is defined in this section. @@ -6251,10 +6220,9 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, DebugOut, Annotations); if (gotInst) { if (!NoShowRawInsn) { - dumpBytes(ArrayRef<uint8_t>(Bytes.data() + Index, Size), outs()); + dumpBytes(makeArrayRef(Bytes.data() + Index, Size), outs()); } formatted_raw_ostream FormattedOS(outs()); - Annotations.flush(); StringRef AnnotationsStr = Annotations.str(); if (isThumb) ThumbIP->printInst(&Inst, FormattedOS, AnnotationsStr, *ThumbSTI); @@ -6316,7 +6284,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, } if (!NoShowRawInsn) { outs() << "\t"; - dumpBytes(ArrayRef<uint8_t>(Bytes.data() + Index, InstSize), outs()); + dumpBytes(makeArrayRef(Bytes.data() + Index, InstSize), outs()); } IP->printInst(&Inst, outs(), "", *STI); outs() << "\n"; @@ -6441,8 +6409,7 @@ static void findUnwindRelocNameAddend(const MachOObjectFile *Obj, // Go back one so that SymbolAddress <= Addr. --Sym; - section_iterator SymSection = Obj->section_end(); - Sym->second.getSection(SymSection); + section_iterator SymSection = *Sym->second.getSection(); if (RelocSection == *SymSection) { // There's a valid symbol in the same section before this reference. ErrorOr<StringRef> NameOrErr = Sym->second.getName(); @@ -6780,13 +6747,268 @@ static void printMachOUnwindInfoSection(const MachOObjectFile *Obj, } } +static unsigned getSizeForEncoding(bool is64Bit, + unsigned symbolEncoding) { + unsigned format = symbolEncoding & 0x0f; + switch (format) { + default: llvm_unreachable("Unknown Encoding"); + case dwarf::DW_EH_PE_absptr: + case dwarf::DW_EH_PE_signed: + return is64Bit ? 8 : 4; + case dwarf::DW_EH_PE_udata2: + case dwarf::DW_EH_PE_sdata2: + return 2; + case dwarf::DW_EH_PE_udata4: + case dwarf::DW_EH_PE_sdata4: + return 4; + case dwarf::DW_EH_PE_udata8: + case dwarf::DW_EH_PE_sdata8: + return 8; + } +} + +static uint64_t readPointer(const char *&Pos, bool is64Bit, unsigned Encoding) { + switch (getSizeForEncoding(is64Bit, Encoding)) { + case 2: + return readNext<uint16_t>(Pos); + break; + case 4: + return readNext<uint32_t>(Pos); + break; + case 8: + return readNext<uint64_t>(Pos); + break; + default: + llvm_unreachable("Illegal data size"); + } +} + +static void printMachOEHFrameSection(const MachOObjectFile *Obj, + std::map<uint64_t, SymbolRef> &Symbols, + const SectionRef &EHFrame) { + if (!Obj->isLittleEndian()) { + outs() << "warning: cannot handle big endian __eh_frame section\n"; + return; + } + + bool is64Bit = Obj->is64Bit(); + + outs() << "Contents of __eh_frame section:\n"; + + StringRef Contents; + EHFrame.getContents(Contents); + + /// A few fields of the CIE are used when decoding the FDE's. This struct + /// will cache those fields we need so that we don't have to decode it + /// repeatedly for each FDE that references it. + struct DecodedCIE { + Optional<uint32_t> FDEPointerEncoding; + Optional<uint32_t> LSDAPointerEncoding; + bool hasAugmentationLength; + }; + + // Map from the start offset of the CIE to the cached data for that CIE. + DenseMap<uint64_t, DecodedCIE> CachedCIEs; + + for (const char *Pos = Contents.data(), *End = Contents.end(); Pos != End; ) { + + const char *EntryStartPos = Pos; + + uint64_t Length = readNext<uint32_t>(Pos); + if (Length == 0xffffffff) + Length = readNext<uint64_t>(Pos); + + // Save the Pos so that we can check the length we encoded against what we + // end up decoding. + const char *PosAfterLength = Pos; + const char *EntryEndPos = PosAfterLength + Length; + + assert(EntryEndPos <= End && + "__eh_frame entry length exceeds section size"); + + uint32_t ID = readNext<uint32_t>(Pos); + if (ID == 0) { + // This is a CIE. + + uint32_t Version = readNext<uint8_t>(Pos); + + // Parse a null terminated augmentation string + SmallString<8> AugmentationString; + for (uint8_t Char = readNext<uint8_t>(Pos); Char; + Char = readNext<uint8_t>(Pos)) + AugmentationString.push_back(Char); + + // Optionally parse the EH data if the augmentation string says it's there. + Optional<uint64_t> EHData; + if (StringRef(AugmentationString).count("eh")) + EHData = is64Bit ? readNext<uint64_t>(Pos) : readNext<uint32_t>(Pos); + + unsigned ULEBByteCount; + uint64_t CodeAlignmentFactor = decodeULEB128((const uint8_t *)Pos, + &ULEBByteCount); + Pos += ULEBByteCount; + + int64_t DataAlignmentFactor = decodeSLEB128((const uint8_t *)Pos, + &ULEBByteCount); + Pos += ULEBByteCount; + + uint32_t ReturnAddressRegister = readNext<uint8_t>(Pos); + + Optional<uint64_t> AugmentationLength; + Optional<uint32_t> LSDAPointerEncoding; + Optional<uint32_t> PersonalityEncoding; + Optional<uint64_t> Personality; + Optional<uint32_t> FDEPointerEncoding; + if (!AugmentationString.empty() && AugmentationString.front() == 'z') { + AugmentationLength = decodeULEB128((const uint8_t *)Pos, + &ULEBByteCount); + Pos += ULEBByteCount; + + // Walk the augmentation string to get all the augmentation data. + for (unsigned i = 1, e = AugmentationString.size(); i != e; ++i) { + char Char = AugmentationString[i]; + switch (Char) { + case 'e': + assert((i + 1) != e && AugmentationString[i + 1] == 'h' && + "Expected 'eh' in augmentation string"); + break; + case 'L': + assert(!LSDAPointerEncoding && "Duplicate LSDA encoding"); + LSDAPointerEncoding = readNext<uint8_t>(Pos); + break; + case 'P': { + assert(!Personality && "Duplicate personality"); + PersonalityEncoding = readNext<uint8_t>(Pos); + Personality = readPointer(Pos, is64Bit, *PersonalityEncoding); + break; + } + case 'R': + assert(!FDEPointerEncoding && "Duplicate FDE encoding"); + FDEPointerEncoding = readNext<uint8_t>(Pos); + break; + case 'z': + llvm_unreachable("'z' must be first in the augmentation string"); + } + } + } + + outs() << "CIE:\n"; + outs() << " Length: " << Length << "\n"; + outs() << " CIE ID: " << ID << "\n"; + outs() << " Version: " << Version << "\n"; + outs() << " Augmentation String: " << AugmentationString << "\n"; + if (EHData) + outs() << " EHData: " << *EHData << "\n"; + outs() << " Code Alignment Factor: " << CodeAlignmentFactor << "\n"; + outs() << " Data Alignment Factor: " << DataAlignmentFactor << "\n"; + outs() << " Return Address Register: " << ReturnAddressRegister << "\n"; + if (AugmentationLength) { + outs() << " Augmentation Data Length: " << *AugmentationLength << "\n"; + if (LSDAPointerEncoding) { + outs() << " FDE LSDA Pointer Encoding: " + << *LSDAPointerEncoding << "\n"; + } + if (Personality) { + outs() << " Personality Encoding: " << *PersonalityEncoding << "\n"; + outs() << " Personality: " << *Personality << "\n"; + } + if (FDEPointerEncoding) { + outs() << " FDE Address Pointer Encoding: " + << *FDEPointerEncoding << "\n"; + } + } + // FIXME: Handle instructions. + // For now just emit some bytes + outs() << " Instructions:\n "; + dumpBytes(makeArrayRef((const uint8_t*)Pos, (const uint8_t*)EntryEndPos), + outs()); + outs() << "\n"; + Pos = EntryEndPos; + + // Cache this entry. + uint64_t Offset = EntryStartPos - Contents.data(); + CachedCIEs[Offset] = { FDEPointerEncoding, LSDAPointerEncoding, + AugmentationLength.hasValue() }; + continue; + } + + // This is an FDE. + // The CIE pointer for an FDE is the same location as the ID which we + // already read. + uint32_t CIEPointer = ID; + + const char *CIEStart = PosAfterLength - CIEPointer; + assert(CIEStart >= Contents.data() && + "FDE points to CIE before the __eh_frame start"); + + uint64_t CIEOffset = CIEStart - Contents.data(); + auto CIEIt = CachedCIEs.find(CIEOffset); + if (CIEIt == CachedCIEs.end()) + llvm_unreachable("Couldn't find CIE at offset in to __eh_frame section"); + + const DecodedCIE &CIE = CIEIt->getSecond(); + assert(CIE.FDEPointerEncoding && + "FDE references CIE which did not set pointer encoding"); + + uint64_t PCPointerSize = getSizeForEncoding(is64Bit, + *CIE.FDEPointerEncoding); + + uint64_t PCBegin = readPointer(Pos, is64Bit, *CIE.FDEPointerEncoding); + uint64_t PCRange = readPointer(Pos, is64Bit, *CIE.FDEPointerEncoding); + + Optional<uint64_t> AugmentationLength; + uint32_t LSDAPointerSize; + Optional<uint64_t> LSDAPointer; + if (CIE.hasAugmentationLength) { + unsigned ULEBByteCount; + AugmentationLength = decodeULEB128((const uint8_t *)Pos, + &ULEBByteCount); + Pos += ULEBByteCount; + + // Decode the LSDA if the CIE augmentation string said we should. + if (CIE.LSDAPointerEncoding) { + LSDAPointerSize = getSizeForEncoding(is64Bit, *CIE.LSDAPointerEncoding); + LSDAPointer = readPointer(Pos, is64Bit, *CIE.LSDAPointerEncoding); + } + } + + outs() << "FDE:\n"; + outs() << " Length: " << Length << "\n"; + outs() << " CIE Offset: " << CIEOffset << "\n"; + + if (PCPointerSize == 8) { + outs() << format(" PC Begin: %016" PRIx64, PCBegin) << "\n"; + outs() << format(" PC Range: %016" PRIx64, PCRange) << "\n"; + } else { + outs() << format(" PC Begin: %08" PRIx64, PCBegin) << "\n"; + outs() << format(" PC Range: %08" PRIx64, PCRange) << "\n"; + } + if (AugmentationLength) { + outs() << " Augmentation Data Length: " << *AugmentationLength << "\n"; + if (LSDAPointer) { + if (LSDAPointerSize == 8) + outs() << format(" LSDA Pointer: %016\n" PRIx64, *LSDAPointer); + else + outs() << format(" LSDA Pointer: %08\n" PRIx64, *LSDAPointer); + } + } + + // FIXME: Handle instructions. + // For now just emit some bytes + outs() << " Instructions:\n "; + dumpBytes(makeArrayRef((const uint8_t*)Pos, (const uint8_t*)EntryEndPos), + outs()); + outs() << "\n"; + Pos = EntryEndPos; + } +} + 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); + section_iterator Section = *SymRef.getSection(); if (Section == Obj->section_end()) continue; @@ -6802,7 +7024,7 @@ void llvm::printMachOUnwindInfo(const MachOObjectFile *Obj) { else if (SectName == "__unwind_info") printMachOUnwindInfoSection(Obj, Symbols, Section); else if (SectName == "__eh_frame") - outs() << "llvm-objdump: warning: unhandled __eh_frame section\n"; + printMachOEHFrameSection(Obj, Symbols, Section); } } @@ -7128,36 +7350,20 @@ static void PrintSegmentCommand(uint32_t cmd, uint32_t cmdsize, 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"; + outs() << " maxprot "; + outs() << ((maxprot & MachO::VM_PROT_READ) ? "r" : "-"); + outs() << ((maxprot & MachO::VM_PROT_WRITE) ? "w" : "-"); + outs() << ((maxprot & MachO::VM_PROT_EXECUTE) ? "x\n" : "-\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"; + outs() << " initprot "; + outs() << ((initprot & MachO::VM_PROT_READ) ? "r" : "-"); + outs() << ((initprot & MachO::VM_PROT_WRITE) ? "w" : "-"); + outs() << ((initprot & MachO::VM_PROT_EXECUTE) ? "x\n" : "-\n"); } } else { outs() << " maxprot " << format("0x%08" PRIx32, maxprot) << "\n"; @@ -7611,26 +7817,11 @@ static void PrintUuidLoadCommand(MachO::uuid_command uuid) { 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]); + for (int i = 0; i < 16; ++i) { + outs() << format("%02" PRIX32, uuid.uuid[i]); + if (i == 3 || i == 5 || i == 7 || i == 9) + outs() << "-"; + } outs() << "\n"; } @@ -7650,30 +7841,47 @@ static void PrintRpathLoadCommand(MachO::rpath_command rpath, const char *Ptr) { } 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"; + StringRef LoadCmdName; + switch (vd.cmd) { + case MachO::LC_VERSION_MIN_MACOSX: + LoadCmdName = "LC_VERSION_MIN_MACOSX"; + break; + case MachO::LC_VERSION_MIN_IPHONEOS: + LoadCmdName = "LC_VERSION_MIN_IPHONEOS"; + break; + case MachO::LC_VERSION_MIN_TVOS: + LoadCmdName = "LC_VERSION_MIN_TVOS"; + break; + case MachO::LC_VERSION_MIN_WATCHOS: + LoadCmdName = "LC_VERSION_MIN_WATCHOS"; + break; + default: + llvm_unreachable("Unknown version min load command"); + } + + outs() << " cmd " << LoadCmdName << '\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() << " version " + << MachOObjectFile::getVersionMinMajor(vd, false) << "." + << MachOObjectFile::getVersionMinMinor(vd, false); + uint32_t Update = MachOObjectFile::getVersionMinUpdate(vd, false); + if (Update != 0) + outs() << "." << Update; outs() << "\n"; if (vd.sdk == 0) outs() << " sdk n/a"; else { - outs() << " sdk " << ((vd.sdk >> 16) & 0xffff) << "." - << ((vd.sdk >> 8) & 0xff); + outs() << " sdk " + << MachOObjectFile::getVersionMinMajor(vd, true) << "." + << MachOObjectFile::getVersionMinMinor(vd, true); } - if ((vd.sdk & 0xff) != 0) - outs() << "." << (vd.sdk & 0xff); + Update = MachOObjectFile::getVersionMinUpdate(vd, true); + if (Update != 0) + outs() << "." << Update; outs() << "\n"; } @@ -8366,7 +8574,9 @@ static void PrintLoadCommands(const MachOObjectFile *Obj, uint32_t filetype, MachO::rpath_command Rpath = Obj->getRpathCommand(Command); PrintRpathLoadCommand(Rpath, Command.Ptr); } else if (Command.C.cmd == MachO::LC_VERSION_MIN_MACOSX || - Command.C.cmd == MachO::LC_VERSION_MIN_IPHONEOS) { + Command.C.cmd == MachO::LC_VERSION_MIN_IPHONEOS || + Command.C.cmd == MachO::LC_VERSION_MIN_TVOS || + Command.C.cmd == MachO::LC_VERSION_MIN_WATCHOS) { MachO::version_min_command Vd = Obj->getVersionMinLoadCommand(Command); PrintVersionMinLoadCommand(Vd); } else if (Command.C.cmd == MachO::LC_SOURCE_VERSION) { @@ -8536,6 +8746,7 @@ public: StringRef segmentName(uint32_t SegIndex); StringRef sectionName(uint32_t SegIndex, uint64_t SegOffset); uint64_t address(uint32_t SegIndex, uint64_t SegOffset); + bool isValidSegIndexAndOffset(uint32_t SegIndex, uint64_t SegOffset); private: struct SectionInfo { @@ -8559,8 +8770,7 @@ SegInfo::SegInfo(const object::MachOObjectFile *Obj) { uint64_t CurSegAddress; for (const SectionRef &Section : Obj->sections()) { SectionInfo Info; - if (error(Section.getName(Info.SectionName))) - return; + error(Section.getName(Info.SectionName)); Info.Address = Section.getAddress(); Info.Size = Section.getSize(); Info.SegmentName = @@ -8585,6 +8795,20 @@ StringRef SegInfo::segmentName(uint32_t SegIndex) { llvm_unreachable("invalid segIndex"); } +bool SegInfo::isValidSegIndexAndOffset(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 true; + } + return false; +} + const SegInfo::SectionInfo &SegInfo::findSection(uint32_t SegIndex, uint64_t OffsetInSeg) { for (const SectionInfo &SI : Sections) { @@ -8753,6 +8977,8 @@ static const char *get_dyld_bind_info_symbolname(uint64_t ReferenceValue, for (const llvm::object::MachOBindEntry &Entry : info->O->bindTable()) { uint32_t SegIndex = Entry.segmentIndex(); uint64_t OffsetInSeg = Entry.segmentOffset(); + if (!sectionTable.isValidSegIndexAndOffset(SegIndex, OffsetInSeg)) + continue; uint64_t Address = sectionTable.address(SegIndex, OffsetInSeg); const char *SymbolName = nullptr; StringRef name = Entry.symbolName(); diff --git a/tools/llvm-objdump/llvm-objdump.cpp b/tools/llvm-objdump/llvm-objdump.cpp index 275eb9c6a454..22167c788901 100644 --- a/tools/llvm-objdump/llvm-objdump.cpp +++ b/tools/llvm-objdump/llvm-objdump.cpp @@ -73,6 +73,13 @@ Disassembled("d", cl::desc("Alias for --disassemble"), cl::aliasopt(Disassemble)); cl::opt<bool> +llvm::DisassembleAll("disassemble-all", + cl::desc("Display assembler mnemonics for the machine instructions")); +static cl::alias +DisassembleAlld("D", cl::desc("Alias for --disassemble-all"), + cl::aliasopt(DisassembleAll)); + +cl::opt<bool> llvm::Relocations("r", cl::desc("Display the relocation entries in the file")); cl::opt<bool> @@ -130,6 +137,13 @@ SectionHeadersShorter("h", cl::desc("Alias for --section-headers"), cl::aliasopt(SectionHeaders)); cl::list<std::string> +llvm::FilterSections("section", cl::desc("Operate on the specified sections only. " + "With -macho dump segment,section")); +cl::alias +static FilterSectionsj("j", cl::desc("Alias for --section"), + cl::aliasopt(llvm::FilterSections)); + +cl::list<std::string> llvm::MAttrs("mattr", cl::CommaSeparated, cl::desc("Target specific attributes"), @@ -163,22 +177,86 @@ cl::opt<bool> PrintFaultMaps("fault-map-section", cl::desc("Display contents of faultmap section")); static StringRef ToolName; -static int ReturnValue = EXIT_SUCCESS; -bool llvm::error(std::error_code EC) { +namespace { +typedef std::function<bool(llvm::object::SectionRef const &)> FilterPredicate; + +class SectionFilterIterator { +public: + SectionFilterIterator(FilterPredicate P, + llvm::object::section_iterator const &I, + llvm::object::section_iterator const &E) + : Predicate(P), Iterator(I), End(E) { + ScanPredicate(); + } + const llvm::object::SectionRef &operator*() const { return *Iterator; } + SectionFilterIterator &operator++() { + ++Iterator; + ScanPredicate(); + return *this; + } + bool operator!=(SectionFilterIterator const &Other) const { + return Iterator != Other.Iterator; + } + +private: + void ScanPredicate() { + while (Iterator != End && !Predicate(*Iterator)) { + ++Iterator; + } + } + FilterPredicate Predicate; + llvm::object::section_iterator Iterator; + llvm::object::section_iterator End; +}; + +class SectionFilter { +public: + SectionFilter(FilterPredicate P, llvm::object::ObjectFile const &O) + : Predicate(P), Object(O) {} + SectionFilterIterator begin() { + return SectionFilterIterator(Predicate, Object.section_begin(), + Object.section_end()); + } + SectionFilterIterator end() { + return SectionFilterIterator(Predicate, Object.section_end(), + Object.section_end()); + } + +private: + FilterPredicate Predicate; + llvm::object::ObjectFile const &Object; +}; +SectionFilter ToolSectionFilter(llvm::object::ObjectFile const &O) { + return SectionFilter([](llvm::object::SectionRef const &S) { + if(FilterSections.empty()) + return true; + llvm::StringRef String; + std::error_code error = S.getName(String); + if (error) + return false; + return std::find(FilterSections.begin(), + FilterSections.end(), + String) != FilterSections.end(); + }, + O); +} +} + +void llvm::error(std::error_code EC) { if (!EC) - return false; + return; - outs() << ToolName << ": error reading file: " << EC.message() << ".\n"; - outs().flush(); - ReturnValue = EXIT_FAILURE; - return true; + errs() << ToolName << ": error reading file: " << EC.message() << ".\n"; + errs().flush(); + exit(1); } -static void report_error(StringRef File, std::error_code EC) { +LLVM_ATTRIBUTE_NORETURN void llvm::report_error(StringRef File, + std::error_code EC) { assert(EC); errs() << ToolName << ": '" << File << "': " << EC.message() << ".\n"; - ReturnValue = EXIT_FAILURE; + exit(1); } static const Target *getTarget(const ObjectFile *Obj = nullptr) { @@ -205,10 +283,8 @@ static const Target *getTarget(const ObjectFile *Obj = nullptr) { std::string Error; const Target *TheTarget = TargetRegistry::lookupTarget(ArchName, TheTriple, Error); - if (!TheTarget) { - errs() << ToolName << ": " << Error; - return nullptr; - } + if (!TheTarget) + report_fatal_error("can't find target: " + Error); // Update the triple name and return the found target. TripleName = TheTriple.getTriple(); @@ -301,11 +377,12 @@ PrettyPrinter &selectPrettyPrinter(Triple const &Triple) { template <class ELFT> static std::error_code getRelocationValueString(const ELFObjectFile<ELFT> *Obj, - DataRefImpl Rel, + const RelocationRef &RelRef, SmallVectorImpl<char> &Result) { + DataRefImpl Rel = RelRef.getRawDataRefImpl(); + typedef typename ELFObjectFile<ELFT>::Elf_Sym Elf_Sym; typedef typename ELFObjectFile<ELFT>::Elf_Shdr Elf_Shdr; - typedef typename ELFObjectFile<ELFT>::Elf_Rel Elf_Rel; typedef typename ELFObjectFile<ELFT>::Elf_Rela Elf_Rela; const ELFFile<ELFT> &EF = *Obj->getELFFile(); @@ -327,36 +404,31 @@ static std::error_code getRelocationValueString(const ELFObjectFile<ELFT> *Obj, if (std::error_code EC = StrTabOrErr.getError()) return EC; StringRef StrTab = *StrTabOrErr; - uint8_t type; + uint8_t type = RelRef.getType(); StringRef res; int64_t addend = 0; - uint16_t symbol_index = 0; switch (Sec->sh_type) { default: return object_error::parse_failed; case ELF::SHT_REL: { - const Elf_Rel *ERel = Obj->getRel(Rel); - type = ERel->getType(EF.isMips64EL()); - symbol_index = ERel->getSymbol(EF.isMips64EL()); // TODO: Read implicit addend from section data. break; } case ELF::SHT_RELA: { const Elf_Rela *ERela = Obj->getRela(Rel); - type = ERela->getType(EF.isMips64EL()); - symbol_index = ERela->getSymbol(EF.isMips64EL()); addend = ERela->r_addend; break; } } - const Elf_Sym *symb = - EF.template getEntry<Elf_Sym>(Sec->sh_link, symbol_index); + symbol_iterator SI = RelRef.getSymbol(); + const Elf_Sym *symb = Obj->getSymbol(SI->getRawDataRefImpl()); StringRef Target; - ErrorOr<const Elf_Shdr *> SymSec = EF.getSection(symb); - if (std::error_code EC = SymSec.getError()) - return EC; if (symb->getType() == ELF::STT_SECTION) { - ErrorOr<StringRef> SecName = EF.getSectionName(*SymSec); + ErrorOr<section_iterator> SymSI = SI->getSection(); + if (std::error_code EC = SymSI.getError()) + return EC; + const Elf_Shdr *SymSec = Obj->getSection((*SymSI)->getRawDataRefImpl()); + ErrorOr<StringRef> SecName = EF.getSectionName(SymSec); if (std::error_code EC = SecName.getError()) return EC; Target = *SecName; @@ -404,6 +476,7 @@ static std::error_code getRelocationValueString(const ELFObjectFile<ELFT> *Obj, break; } case ELF::EM_386: + case ELF::EM_IAMCU: case ELF::EM_ARM: case ELF::EM_HEXAGON: case ELF::EM_MIPS: @@ -418,9 +491,8 @@ static std::error_code getRelocationValueString(const ELFObjectFile<ELFT> *Obj, } static std::error_code getRelocationValueString(const ELFObjectFileBase *Obj, - const RelocationRef &RelRef, + const RelocationRef &Rel, SmallVectorImpl<char> &Result) { - DataRefImpl Rel = RelRef.getRawDataRefImpl(); if (auto *ELF32LE = dyn_cast<ELF32LEObjectFile>(Obj)) return getRelocationValueString(ELF32LE, Rel, Result); if (auto *ELF64LE = dyn_cast<ELF64LEObjectFile>(Obj)) @@ -471,7 +543,7 @@ static void printRelocationTargetName(const MachOObjectFile *O, // If we couldn't find a symbol that this relocation refers to, try // to find a section beginning instead. - for (const SectionRef &Section : O->sections()) { + for (const SectionRef &Section : ToolSectionFilter(*O)) { std::error_code ec; StringRef Name; @@ -496,8 +568,8 @@ static void printRelocationTargetName(const MachOObjectFile *O, symbol_iterator SI = O->symbol_begin(); advance(SI, Val); ErrorOr<StringRef> SOrErr = SI->getName(); - if (!error(SOrErr.getError())) - S = *SOrErr; + error(SOrErr.getError()); + S = *SOrErr; } else { section_iterator SI = O->section_begin(); // Adjust for the fact that sections are 1-indexed. @@ -732,10 +804,6 @@ static bool getHidden(RelocationRef RelRef) { static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { const Target *TheTarget = getTarget(Obj); - // getTarget() will have already issued a diagnostic if necessary, so - // just bail here if it failed. - if (!TheTarget) - return; // Package up features to be passed to target/subtarget std::string FeaturesStr; @@ -748,42 +816,28 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { std::unique_ptr<const MCRegisterInfo> MRI( TheTarget->createMCRegInfo(TripleName)); - if (!MRI) { - errs() << "error: no register info for target " << TripleName << "\n"; - return; - } + if (!MRI) + report_fatal_error("error: no register info for target " + TripleName); // Set up disassembler. std::unique_ptr<const MCAsmInfo> AsmInfo( TheTarget->createMCAsmInfo(*MRI, TripleName)); - if (!AsmInfo) { - errs() << "error: no assembly info for target " << TripleName << "\n"; - return; - } - + if (!AsmInfo) + report_fatal_error("error: no assembly info for target " + TripleName); std::unique_ptr<const MCSubtargetInfo> STI( TheTarget->createMCSubtargetInfo(TripleName, MCPU, FeaturesStr)); - if (!STI) { - errs() << "error: no subtarget info for target " << TripleName << "\n"; - return; - } - + if (!STI) + report_fatal_error("error: no subtarget info for target " + TripleName); std::unique_ptr<const MCInstrInfo> MII(TheTarget->createMCInstrInfo()); - if (!MII) { - errs() << "error: no instruction info for target " << TripleName << "\n"; - return; - } - + if (!MII) + report_fatal_error("error: no instruction info for target " + TripleName); std::unique_ptr<const MCObjectFileInfo> MOFI(new MCObjectFileInfo); MCContext Ctx(AsmInfo.get(), MRI.get(), MOFI.get()); std::unique_ptr<MCDisassembler> DisAsm( TheTarget->createMCDisassembler(*STI, Ctx)); - - if (!DisAsm) { - errs() << "error: no disassembler for target " << TripleName << "\n"; - return; - } + if (!DisAsm) + report_fatal_error("error: no disassembler for target " + TripleName); std::unique_ptr<const MCInstrAnalysis> MIA( TheTarget->createMCInstrAnalysis(MII.get())); @@ -791,11 +845,9 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { int AsmPrinterVariant = AsmInfo->getAssemblerDialect(); std::unique_ptr<MCInstPrinter> IP(TheTarget->createMCInstPrinter( Triple(TripleName), AsmPrinterVariant, *AsmInfo, *MII, *MRI)); - if (!IP) { - errs() << "error: no instruction printer for target " << TripleName - << '\n'; - return; - } + if (!IP) + report_fatal_error("error: no instruction printer for target " + + TripleName); IP->setPrintImmHex(PrintImmHex); PrettyPrinter &PIP = selectPrettyPrinter(Triple(TripleName)); @@ -806,38 +858,75 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { // in RelocSecs contain the relocations for section S. std::error_code EC; std::map<SectionRef, SmallVector<SectionRef, 1>> SectionRelocMap; - for (const SectionRef &Section : Obj->sections()) { + for (const SectionRef &Section : ToolSectionFilter(*Obj)) { section_iterator Sec2 = Section.getRelocatedSection(); if (Sec2 != Obj->section_end()) SectionRelocMap[*Sec2].push_back(Section); } // Create a mapping from virtual address to symbol name. This is used to - // pretty print the target of a call. - std::vector<std::pair<uint64_t, StringRef>> AllSymbols; - if (MIA) { - for (const SymbolRef &Symbol : Obj->symbols()) { - if (Symbol.getType() != SymbolRef::ST_Function) - continue; + // pretty print the symbols while disassembling. + typedef std::vector<std::pair<uint64_t, StringRef>> SectionSymbolsTy; + std::map<SectionRef, SectionSymbolsTy> AllSymbols; + for (const SymbolRef &Symbol : Obj->symbols()) { + ErrorOr<uint64_t> AddressOrErr = Symbol.getAddress(); + error(AddressOrErr.getError()); + uint64_t Address = *AddressOrErr; - ErrorOr<uint64_t> AddressOrErr = Symbol.getAddress(); - if (error(AddressOrErr.getError())) - break; - uint64_t Address = *AddressOrErr; + ErrorOr<StringRef> Name = Symbol.getName(); + error(Name.getError()); + if (Name->empty()) + continue; - ErrorOr<StringRef> Name = Symbol.getName(); - if (error(Name.getError())) - break; - if (Name->empty()) + ErrorOr<section_iterator> SectionOrErr = Symbol.getSection(); + error(SectionOrErr.getError()); + section_iterator SecI = *SectionOrErr; + if (SecI == Obj->section_end()) + continue; + + AllSymbols[*SecI].emplace_back(Address, *Name); + } + + // Create a mapping from virtual address to section. + std::vector<std::pair<uint64_t, SectionRef>> SectionAddresses; + for (SectionRef Sec : Obj->sections()) + SectionAddresses.emplace_back(Sec.getAddress(), Sec); + array_pod_sort(SectionAddresses.begin(), SectionAddresses.end()); + + // Linked executables (.exe and .dll files) typically don't include a real + // symbol table but they might contain an export table. + if (const auto *COFFObj = dyn_cast<COFFObjectFile>(Obj)) { + for (const auto &ExportEntry : COFFObj->export_directories()) { + StringRef Name; + error(ExportEntry.getSymbolName(Name)); + if (Name.empty()) continue; - AllSymbols.push_back(std::make_pair(Address, *Name)); - } + uint32_t RVA; + error(ExportEntry.getExportRVA(RVA)); + + uint64_t VA = COFFObj->getImageBase() + RVA; + auto Sec = std::upper_bound( + SectionAddresses.begin(), SectionAddresses.end(), VA, + [](uint64_t LHS, const std::pair<uint64_t, SectionRef> &RHS) { + return LHS < RHS.first; + }); + if (Sec != SectionAddresses.begin()) + --Sec; + else + Sec = SectionAddresses.end(); - array_pod_sort(AllSymbols.begin(), AllSymbols.end()); + if (Sec != SectionAddresses.end()) + AllSymbols[Sec->second].emplace_back(VA, Name); + } } - for (const SectionRef &Section : Obj->sections()) { - if (!Section.isText() || Section.isVirtual()) + // Sort all the symbols, this allows us to use a simple binary search to find + // a symbol near an address. + for (std::pair<const SectionRef, SectionSymbolsTy> &SecSyms : AllSymbols) + array_pod_sort(SecSyms.second.begin(), SecSyms.second.end()); + + for (const SectionRef &Section : ToolSectionFilter(*Obj)) { + if (!DisassembleAll && (!Section.isText() || Section.isVirtual())) continue; uint64_t SectionAddr = Section.getAddress(); @@ -845,27 +934,23 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { 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()) { - if (Section.containsSymbol(Symbol)) { - ErrorOr<uint64_t> AddressOrErr = Symbol.getAddress(); - if (error(AddressOrErr.getError())) - break; - uint64_t Address = *AddressOrErr; - Address -= SectionAddr; - if (Address >= SectSize) - continue; - - ErrorOr<StringRef> Name = Symbol.getName(); - if (error(Name.getError())) - break; - Symbols.push_back(std::make_pair(Address, *Name)); + // Get the list of all the symbols in this section. + SectionSymbolsTy &Symbols = AllSymbols[Section]; + std::vector<uint64_t> DataMappingSymsAddr; + std::vector<uint64_t> TextMappingSymsAddr; + if (Obj->isELF() && Obj->getArch() == Triple::aarch64) { + for (const auto &Symb : Symbols) { + uint64_t Address = Symb.first; + StringRef Name = Symb.second; + if (Name.startswith("$d")) + DataMappingSymsAddr.push_back(Address - SectionAddr); + if (Name.startswith("$x")) + TextMappingSymsAddr.push_back(Address - SectionAddr); } } - // Sort the symbols by address, just in case they didn't come in that way. - array_pod_sort(Symbols.begin(), Symbols.end()); + std::sort(DataMappingSymsAddr.begin(), DataMappingSymsAddr.end()); + std::sort(TextMappingSymsAddr.begin(), TextMappingSymsAddr.end()); // Make a list of all the relocations for this section. std::vector<RelocationRef> Rels; @@ -886,8 +971,7 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { SegmentName = MachO->getSectionFinalSegmentName(DR); } StringRef name; - if (error(Section.getName(name))) - break; + error(Section.getName(name)); outs() << "Disassembly of section "; if (!SegmentName.empty()) outs() << SegmentName << ","; @@ -895,14 +979,13 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { // If the section has no symbol at the start, just insert a dummy one. if (Symbols.empty() || Symbols[0].first != 0) - Symbols.insert(Symbols.begin(), std::make_pair(0, name)); + Symbols.insert(Symbols.begin(), std::make_pair(SectionAddr, name)); SmallString<40> Comments; raw_svector_ostream CommentStream(Comments); StringRef BytesStr; - if (error(Section.getContents(BytesStr))) - break; + error(Section.getContents(BytesStr)); ArrayRef<uint8_t> Bytes(reinterpret_cast<const uint8_t *>(BytesStr.data()), BytesStr.size()); @@ -914,11 +997,16 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { // Disassemble symbol by symbol. for (unsigned si = 0, se = Symbols.size(); si != se; ++si) { - uint64_t Start = Symbols[si].first; - // 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; + uint64_t Start = Symbols[si].first - SectionAddr; + // 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 - SectionAddr; + // Don't try to disassemble beyond the end of section contents. + if (End > SectSize) + End = SectSize; // If this symbol has the same address as the next symbol, then skip it. - if (Start == End) + if (Start >= End) continue; outs() << '\n' << Symbols[si].second << ":\n"; @@ -932,6 +1020,45 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { for (Index = Start; Index < End; Index += Size) { MCInst Inst; + // AArch64 ELF binaries can interleave data and text in the + // same section. We rely on the markers introduced to + // understand what we need to dump. + if (Obj->isELF() && Obj->getArch() == Triple::aarch64) { + uint64_t Stride = 0; + + auto DAI = std::lower_bound(DataMappingSymsAddr.begin(), + DataMappingSymsAddr.end(), Index); + if (DAI != DataMappingSymsAddr.end() && *DAI == Index) { + // Switch to data. + while (Index < End) { + outs() << format("%8" PRIx64 ":", SectionAddr + Index); + outs() << "\t"; + if (Index + 4 <= End) { + Stride = 4; + dumpBytes(Bytes.slice(Index, 4), outs()); + outs() << "\t.word"; + } else if (Index + 2 <= End) { + Stride = 2; + dumpBytes(Bytes.slice(Index, 2), outs()); + outs() << "\t.short"; + } else { + Stride = 1; + dumpBytes(Bytes.slice(Index, 1), outs()); + outs() << "\t.byte"; + } + Index += Stride; + outs() << "\n"; + auto TAI = std::lower_bound(TextMappingSymsAddr.begin(), + TextMappingSymsAddr.end(), Index); + if (TAI != TextMappingSymsAddr.end() && *TAI == Index) + break; + } + } + } + + if (Index >= End) + break; + if (DisAsm->getInstruction(Inst, Size, Bytes.slice(Index), SectionAddr + Index, DebugOut, CommentStream)) { @@ -940,26 +1067,55 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { SectionAddr + Index, outs(), "", *STI); outs() << CommentStream.str(); Comments.clear(); + + // Try to resolve the target of a call, tail call, etc. to a specific + // symbol. if (MIA && (MIA->isCall(Inst) || MIA->isUnconditionalBranch(Inst) || MIA->isConditionalBranch(Inst))) { uint64_t Target; if (MIA->evaluateBranch(Inst, SectionAddr + Index, Size, Target)) { - auto TargetSym = std::upper_bound( - AllSymbols.begin(), AllSymbols.end(), Target, - [](uint64_t LHS, const std::pair<uint64_t, StringRef> &RHS) { - return LHS < RHS.first; - }); - if (TargetSym != AllSymbols.begin()) - --TargetSym; - else - TargetSym = AllSymbols.end(); + // In a relocatable object, the target's section must reside in + // the same section as the call instruction or it is accessed + // through a relocation. + // + // In a non-relocatable object, the target may be in any section. + // + // N.B. We don't walk the relocations in the relocatable case yet. + auto *TargetSectionSymbols = &Symbols; + if (!Obj->isRelocatableObject()) { + auto SectionAddress = std::upper_bound( + SectionAddresses.begin(), SectionAddresses.end(), Target, + [](uint64_t LHS, + const std::pair<uint64_t, SectionRef> &RHS) { + return LHS < RHS.first; + }); + if (SectionAddress != SectionAddresses.begin()) { + --SectionAddress; + TargetSectionSymbols = &AllSymbols[SectionAddress->second]; + } else { + TargetSectionSymbols = nullptr; + } + } - if (TargetSym != AllSymbols.end()) { - outs() << " <" << TargetSym->second; - uint64_t Disp = Target - TargetSym->first; - if (Disp) - outs() << '+' << utohexstr(Disp); - outs() << '>'; + // Find the first symbol in the section whose offset is less than + // or equal to the target. + if (TargetSectionSymbols) { + auto TargetSym = std::upper_bound( + TargetSectionSymbols->begin(), TargetSectionSymbols->end(), + Target, [](uint64_t LHS, + const std::pair<uint64_t, StringRef> &RHS) { + return LHS < RHS.first; + }); + if (TargetSym != TargetSectionSymbols->begin()) { + --TargetSym; + uint64_t TargetAddress = std::get<0>(*TargetSym); + StringRef TargetName = std::get<1>(*TargetSym); + outs() << " <" << TargetName; + uint64_t Disp = Target - TargetAddress; + if (Disp) + outs() << '+' << utohexstr(Disp); + outs() << '>'; + } } } } @@ -983,8 +1139,7 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { // Stop when rel_cur's address is past the current instruction. if (addr >= Index + Size) break; rel_cur->getTypeName(name); - if (error(getRelocationValueString(*rel_cur, val))) - goto skip_print_rel; + error(getRelocationValueString(*rel_cur, val)); outs() << format(Fmt.data(), SectionAddr + addr) << name << "\t" << val << "\n"; @@ -1004,12 +1159,11 @@ void llvm::PrintRelocations(const ObjectFile *Obj) { if (!Obj->isRelocatableObject()) return; - for (const SectionRef &Section : Obj->sections()) { + for (const SectionRef &Section : ToolSectionFilter(*Obj)) { if (Section.relocation_begin() == Section.relocation_end()) continue; StringRef secname; - if (error(Section.getName(secname))) - continue; + error(Section.getName(secname)); outs() << "RELOCATION RECORDS FOR [" << secname << "]:\n"; for (const RelocationRef &Reloc : Section.relocations()) { bool hidden = getHidden(Reloc); @@ -1019,8 +1173,7 @@ void llvm::PrintRelocations(const ObjectFile *Obj) { if (hidden) continue; Reloc.getTypeName(relocname); - if (error(getRelocationValueString(Reloc, valuestr))) - continue; + error(getRelocationValueString(Reloc, valuestr)); outs() << format(Fmt.data(), address) << " " << relocname << " " << valuestr << "\n"; } @@ -1032,10 +1185,9 @@ void llvm::PrintSectionHeaders(const ObjectFile *Obj) { outs() << "Sections:\n" "Idx Name Size Address Type\n"; unsigned i = 0; - for (const SectionRef &Section : Obj->sections()) { + for (const SectionRef &Section : ToolSectionFilter(*Obj)) { StringRef Name; - if (error(Section.getName(Name))) - return; + error(Section.getName(Name)); uint64_t Address = Section.getAddress(); uint64_t Size = Section.getSize(); bool Text = Section.isText(); @@ -1051,11 +1203,10 @@ void llvm::PrintSectionHeaders(const ObjectFile *Obj) { void llvm::PrintSectionContents(const ObjectFile *Obj) { std::error_code EC; - for (const SectionRef &Section : Obj->sections()) { + for (const SectionRef &Section : ToolSectionFilter(*Obj)) { StringRef Name; StringRef Contents; - if (error(Section.getName(Name))) - continue; + error(Section.getName(Name)); uint64_t BaseAddr = Section.getAddress(); uint64_t Size = Section.getSize(); if (!Size) @@ -1069,8 +1220,7 @@ void llvm::PrintSectionContents(const ObjectFile *Obj) { continue; } - if (error(Section.getContents(Contents))) - continue; + error(Section.getContents(Contents)); // Dump out the content as hex and printable ascii characters. for (std::size_t addr = 0, end = Contents.size(); addr < end; addr += 16) { @@ -1098,84 +1248,28 @@ void llvm::PrintSectionContents(const ObjectFile *Obj) { } } -static void PrintCOFFSymbolTable(const COFFObjectFile *coff) { - for (unsigned SI = 0, SE = coff->getNumberOfSymbols(); SI != SE; ++SI) { - ErrorOr<COFFSymbolRef> Symbol = coff->getSymbol(SI); - StringRef Name; - if (error(Symbol.getError())) - return; - - if (error(coff->getSymbolName(*Symbol, Name))) - return; - - outs() << "[" << format("%2d", SI) << "]" - << "(sec " << format("%2d", int(Symbol->getSectionNumber())) << ")" - << "(fl 0x00)" // Flag bits, which COFF doesn't have. - << "(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->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) - , unsigned(asd->NumberOfRelocations) - , unsigned(asd->NumberOfLinenumbers) - , unsigned(asd->CheckSum)) - << format("assoc %d comdat %d\n" - , unsigned(AuxNumber) - , unsigned(asd->Selection)); - } else if (Symbol->isFileRecord()) { - const char *FileName; - if (error(coff->getAuxSymbol<char>(SI + 1, FileName))) - return; - - StringRef Name(FileName, Symbol->getNumberOfAuxSymbols() * - coff->getSymbolTableEntrySize()); - outs() << "AUX " << Name.rtrim(StringRef("\0", 1)) << '\n'; - - SI = SI + Symbol->getNumberOfAuxSymbols(); - break; - } else { - outs() << "AUX Unknown\n"; - } - } - } -} - void llvm::PrintSymbolTable(const ObjectFile *o) { outs() << "SYMBOL TABLE:\n"; if (const COFFObjectFile *coff = dyn_cast<const COFFObjectFile>(o)) { - PrintCOFFSymbolTable(coff); + printCOFFSymbolTable(coff); return; } for (const SymbolRef &Symbol : o->symbols()) { ErrorOr<uint64_t> AddressOrError = Symbol.getAddress(); - if (error(AddressOrError.getError())) - continue; + error(AddressOrError.getError()); uint64_t Address = *AddressOrError; SymbolRef::Type Type = Symbol.getType(); uint32_t Flags = Symbol.getFlags(); - section_iterator Section = o->section_end(); - if (error(Symbol.getSection(Section))) - continue; + ErrorOr<section_iterator> SectionOrErr = Symbol.getSection(); + error(SectionOrErr.getError()); + section_iterator Section = *SectionOrErr; StringRef Name; if (Type == SymbolRef::ST_Debug && Section != o->section_end()) { Section->getName(Name); } else { ErrorOr<StringRef> NameOrErr = Symbol.getName(); - if (error(NameOrErr.getError())) - continue; + error(NameOrErr.getError()); Name = *NameOrErr; } @@ -1222,8 +1316,7 @@ void llvm::PrintSymbolTable(const ObjectFile *o) { outs() << SegmentName << ","; } StringRef SectionName; - if (error(Section->getName(SectionName))) - SectionName = ""; + error(Section->getName(SectionName)); outs() << SectionName; } @@ -1329,7 +1422,7 @@ void llvm::printRawClangAST(const ObjectFile *Obj) { } Optional<object::SectionRef> ClangASTSection; - for (auto Sec : Obj->sections()) { + for (auto Sec : ToolSectionFilter(*Obj)) { StringRef Name; Sec.getName(Name); if (Name == ClangASTSectionName) { @@ -1341,11 +1434,7 @@ void llvm::printRawClangAST(const ObjectFile *Obj) { return; StringRef ClangASTContents; - if (error(ClangASTSection.getValue().getContents(ClangASTContents))) { - errs() << "Could not read the " << ClangASTSectionName << " section!\n"; - return; - } - + error(ClangASTSection.getValue().getContents(ClangASTContents)); outs().write(ClangASTContents.data(), ClangASTContents.size()); } @@ -1364,7 +1453,7 @@ static void printFaultMaps(const ObjectFile *Obj) { Optional<object::SectionRef> FaultMapSection; - for (auto Sec : Obj->sections()) { + for (auto Sec : ToolSectionFilter(*Obj)) { StringRef Name; Sec.getName(Name); if (Name == FaultMapSectionName) { @@ -1381,10 +1470,7 @@ static void printFaultMaps(const ObjectFile *Obj) { } StringRef FaultMapContents; - if (error(FaultMapSection.getValue().getContents(FaultMapContents))) { - errs() << "Could not read the " << FaultMapContents << " section!\n"; - return; - } + error(FaultMapSection.getValue().getContents(FaultMapContents)); FaultMapParser FMP(FaultMapContents.bytes_begin(), FaultMapContents.bytes_end()); @@ -1393,13 +1479,14 @@ static void printFaultMaps(const ObjectFile *Obj) { } static void printPrivateFileHeader(const ObjectFile *o) { - if (o->isELF()) { + if (o->isELF()) printELFFileHeader(o); - } else if (o->isCOFF()) { + else if (o->isCOFF()) printCOFFFileHeader(o); - } else if (o->isMachO()) { + else if (o->isMachO()) printMachOFileHeader(o); - } + else + report_fatal_error("Invalid/Unsupported object file format"); } static void DumpObject(const ObjectFile *o) { @@ -1442,15 +1529,14 @@ static void DumpObject(const ObjectFile *o) { /// @brief Dump each object file in \a a; static void DumpArchive(const Archive *a) { - for (Archive::child_iterator i = a->child_begin(), e = a->child_end(); i != e; - ++i) { - ErrorOr<std::unique_ptr<Binary>> ChildOrErr = i->getAsBinary(); - if (std::error_code EC = ChildOrErr.getError()) { - // Ignore non-object files. + for (auto &ErrorOrChild : a->children()) { + if (std::error_code EC = ErrorOrChild.getError()) + report_error(a->getFileName(), EC); + const Archive::Child &C = *ErrorOrChild; + ErrorOr<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); + if (std::error_code EC = ChildOrErr.getError()) if (EC != object_error::invalid_file_type) report_error(a->getFileName(), EC); - continue; - } if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) DumpObject(o); else @@ -1460,11 +1546,6 @@ static void DumpArchive(const Archive *a) { /// @brief Open file and figure out how to dump it. static void DumpInput(StringRef file) { - // If file isn't stdin, check that it exists. - if (file != "-" && !sys::fs::exists(file)) { - report_error(file, errc::no_such_file_or_directory); - return; - } // If we are using the Mach-O specific object file parser, then let it parse // the file and process the command line options. So the -arch flags can @@ -1476,10 +1557,8 @@ static void DumpInput(StringRef file) { // Attempt to open the binary. ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(file); - if (std::error_code EC = BinaryOrErr.getError()) { + if (std::error_code EC = BinaryOrErr.getError()) report_error(file, EC); - return; - } Binary &Binary = *BinaryOrErr.get().getBinary(); if (Archive *a = dyn_cast<Archive>(&Binary)) @@ -1499,7 +1578,6 @@ int main(int argc, char **argv) { // Initialize targets and assembly printers/parsers. llvm::InitializeAllTargetInfos(); llvm::InitializeAllTargetMCs(); - llvm::InitializeAllAsmParsers(); llvm::InitializeAllDisassemblers(); // Register the target printer for --version. @@ -1514,6 +1592,8 @@ int main(int argc, char **argv) { if (InputFilenames.size() == 0) InputFilenames.push_back("a.out"); + if (DisassembleAll) + Disassemble = true; if (!Disassemble && !Relocations && !SectionHeaders @@ -1536,7 +1616,7 @@ int main(int argc, char **argv) { && !(DylibsUsed && MachOOpt) && !(DylibId && MachOOpt) && !(ObjcMetaData && MachOOpt) - && !(DumpSections.size() != 0 && MachOOpt) + && !(FilterSections.size() != 0 && MachOOpt) && !PrintFaultMaps) { cl::PrintHelpMessage(); return 2; @@ -1545,5 +1625,5 @@ int main(int argc, char **argv) { std::for_each(InputFilenames.begin(), InputFilenames.end(), DumpInput); - return ReturnValue; + return EXIT_SUCCESS; } diff --git a/tools/llvm-objdump/llvm-objdump.h b/tools/llvm-objdump/llvm-objdump.h index eb10d8344f71..6e8ad6bda156 100644 --- a/tools/llvm-objdump/llvm-objdump.h +++ b/tools/llvm-objdump/llvm-objdump.h @@ -11,6 +11,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compiler.h" #include "llvm/Support/DataTypes.h" namespace llvm { @@ -25,8 +26,9 @@ extern cl::opt<std::string> TripleName; extern cl::opt<std::string> ArchName; extern cl::opt<std::string> MCPU; extern cl::list<std::string> MAttrs; -extern cl::list<std::string> DumpSections; +extern cl::list<std::string> FilterSections; extern cl::opt<bool> Disassemble; +extern cl::opt<bool> DisassembleAll; extern cl::opt<bool> NoShowRawInsn; extern cl::opt<bool> PrivateHeaders; extern cl::opt<bool> ExportsTrie; @@ -54,7 +56,7 @@ extern cl::opt<bool> UnwindInfo; extern cl::opt<bool> PrintImmHex; // Various helper functions. -bool error(std::error_code ec); +void error(std::error_code ec); bool RelocAddressLess(object::RelocationRef a, object::RelocationRef b); void ParseInputMachO(StringRef Filename); void printCOFFUnwindInfo(const object::COFFObjectFile* o); @@ -66,6 +68,7 @@ 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 printCOFFSymbolTable(const object::COFFObjectFile *o); void printMachOFileHeader(const object::ObjectFile *o); void printExportsTrie(const object::ObjectFile *o); void printRebaseTable(const object::ObjectFile *o); @@ -77,6 +80,7 @@ void PrintRelocations(const object::ObjectFile *o); void PrintSectionHeaders(const object::ObjectFile *o); void PrintSectionContents(const object::ObjectFile *o); void PrintSymbolTable(const object::ObjectFile *o); +LLVM_ATTRIBUTE_NORETURN void report_error(StringRef File, std::error_code EC); } // end namespace llvm diff --git a/tools/llvm-pdbdump/BuiltinDumper.cpp b/tools/llvm-pdbdump/BuiltinDumper.cpp index d80829841f3a..43270540f453 100644 --- a/tools/llvm-pdbdump/BuiltinDumper.cpp +++ b/tools/llvm-pdbdump/BuiltinDumper.cpp @@ -19,69 +19,53 @@ BuiltinDumper::BuiltinDumper(LinePrinter &P) : PDBSymDumper(false), Printer(P) {} void BuiltinDumper::start(const PDBSymbolTypeBuiltin &Symbol) { + WithColor(Printer, PDB_ColorItem::Type).get() << getTypeName(Symbol); +} + +StringRef BuiltinDumper::getTypeName(const PDBSymbolTypeBuiltin &Symbol) { PDB_BuiltinType Type = Symbol.getBuiltinType(); switch (Type) { case PDB_BuiltinType::Float: if (Symbol.getLength() == 4) - WithColor(Printer, PDB_ColorItem::Type).get() << "float"; - else - WithColor(Printer, PDB_ColorItem::Type).get() << "double"; - break; + return "float"; + return "double"; case PDB_BuiltinType::UInt: - WithColor(Printer, PDB_ColorItem::Type).get() << "unsigned"; if (Symbol.getLength() == 8) - WithColor(Printer, PDB_ColorItem::Type).get() << " __int64"; - break; + return "unsigned __int64"; + return "unsigned"; case PDB_BuiltinType::Int: if (Symbol.getLength() == 4) - WithColor(Printer, PDB_ColorItem::Type).get() << "int"; - else - WithColor(Printer, PDB_ColorItem::Type).get() << "__int64"; - break; + return "int"; + return "__int64"; case PDB_BuiltinType::Char: - WithColor(Printer, PDB_ColorItem::Type).get() << "char"; - break; + return "char"; case PDB_BuiltinType::WCharT: - WithColor(Printer, PDB_ColorItem::Type).get() << "wchar_t"; - break; + return "wchar_t"; case PDB_BuiltinType::Void: - WithColor(Printer, PDB_ColorItem::Type).get() << "void"; - break; + return "void"; case PDB_BuiltinType::Long: - WithColor(Printer, PDB_ColorItem::Type).get() << "long"; - break; + return "long"; case PDB_BuiltinType::ULong: - WithColor(Printer, PDB_ColorItem::Type).get() << "unsigned long"; - break; + return "unsigned long"; case PDB_BuiltinType::Bool: - WithColor(Printer, PDB_ColorItem::Type).get() << "bool"; - break; + return "bool"; case PDB_BuiltinType::Currency: - WithColor(Printer, PDB_ColorItem::Type).get() << "CURRENCY"; - break; + return "CURRENCY"; case PDB_BuiltinType::Date: - WithColor(Printer, PDB_ColorItem::Type).get() << "DATE"; - break; + return "DATE"; case PDB_BuiltinType::Variant: - WithColor(Printer, PDB_ColorItem::Type).get() << "VARIANT"; - break; + return "VARIANT"; case PDB_BuiltinType::Complex: - WithColor(Printer, PDB_ColorItem::Type).get() << "complex"; - break; + return "complex"; case PDB_BuiltinType::Bitfield: - WithColor(Printer, PDB_ColorItem::Type).get() << "bitfield"; - break; + return "bitfield"; case PDB_BuiltinType::BSTR: - WithColor(Printer, PDB_ColorItem::Type).get() << "BSTR"; - break; + return "BSTR"; case PDB_BuiltinType::HResult: - WithColor(Printer, PDB_ColorItem::Type).get() << "HRESULT"; - break; + return "HRESULT"; case PDB_BuiltinType::BCD: - WithColor(Printer, PDB_ColorItem::Type).get() << "HRESULT"; - break; + return "HRESULT"; default: - WithColor(Printer, PDB_ColorItem::Type).get() << "void"; - break; + return "void"; } } diff --git a/tools/llvm-pdbdump/BuiltinDumper.h b/tools/llvm-pdbdump/BuiltinDumper.h index 8cf984a0ca61..ac666dbd059b 100644 --- a/tools/llvm-pdbdump/BuiltinDumper.h +++ b/tools/llvm-pdbdump/BuiltinDumper.h @@ -23,6 +23,8 @@ public: void start(const PDBSymbolTypeBuiltin &Symbol); private: + StringRef getTypeName(const PDBSymbolTypeBuiltin &Symbol); + LinePrinter &Printer; }; } diff --git a/tools/llvm-pdbdump/LinePrinter.cpp b/tools/llvm-pdbdump/LinePrinter.cpp index 6bbc403f5caf..a43727f02b5e 100644 --- a/tools/llvm-pdbdump/LinePrinter.cpp +++ b/tools/llvm-pdbdump/LinePrinter.cpp @@ -11,19 +11,49 @@ #include "llvm-pdbdump.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/Support/Regex.h" #include <algorithm> +namespace { +bool IsItemExcluded(llvm::StringRef Item, + std::list<llvm::Regex> &IncludeFilters, + std::list<llvm::Regex> &ExcludeFilters) { + if (Item.empty()) + return false; + + auto match_pred = [Item](llvm::Regex &R) { return R.match(Item); }; + + // Include takes priority over exclude. If the user specified include + // filters, and none of them include this item, them item is gone. + if (!IncludeFilters.empty() && !any_of(IncludeFilters, match_pred)) + return true; + + if (any_of(ExcludeFilters, match_pred)) + return true; + + return false; +} +} + using namespace llvm; LinePrinter::LinePrinter(int Indent, llvm::raw_ostream &Stream) : OS(Stream), IndentSpaces(Indent), CurrentIndent(0) { - SetFilters(TypeFilters, opts::ExcludeTypes.begin(), opts::ExcludeTypes.end()); - SetFilters(SymbolFilters, opts::ExcludeSymbols.begin(), + SetFilters(ExcludeTypeFilters, opts::ExcludeTypes.begin(), + opts::ExcludeTypes.end()); + SetFilters(ExcludeSymbolFilters, opts::ExcludeSymbols.begin(), opts::ExcludeSymbols.end()); - SetFilters(CompilandFilters, opts::ExcludeCompilands.begin(), + SetFilters(ExcludeCompilandFilters, opts::ExcludeCompilands.begin(), opts::ExcludeCompilands.end()); + + SetFilters(IncludeTypeFilters, opts::IncludeTypes.begin(), + opts::IncludeTypes.end()); + SetFilters(IncludeSymbolFilters, opts::IncludeSymbols.begin(), + opts::IncludeSymbols.end()); + SetFilters(IncludeCompilandFilters, opts::IncludeCompilands.begin(), + opts::IncludeCompilands.end()); } void LinePrinter::Indent() { CurrentIndent += IndentSpaces; } @@ -38,87 +68,53 @@ void LinePrinter::NewLine() { } bool LinePrinter::IsTypeExcluded(llvm::StringRef TypeName) { - if (TypeName.empty()) - return false; - - for (auto &Expr : TypeFilters) { - if (Expr.match(TypeName)) - return true; - } - return false; + return IsItemExcluded(TypeName, IncludeTypeFilters, ExcludeTypeFilters); } bool LinePrinter::IsSymbolExcluded(llvm::StringRef SymbolName) { - if (SymbolName.empty()) - return false; - - for (auto &Expr : SymbolFilters) { - if (Expr.match(SymbolName)) - return true; - } - return false; + return IsItemExcluded(SymbolName, IncludeSymbolFilters, ExcludeSymbolFilters); } bool LinePrinter::IsCompilandExcluded(llvm::StringRef CompilandName) { - if (CompilandName.empty()) - return false; - - for (auto &Expr : CompilandFilters) { - if (Expr.match(CompilandName)) - return true; - } - return false; + return IsItemExcluded(CompilandName, IncludeCompilandFilters, + ExcludeCompilandFilters); } WithColor::WithColor(LinePrinter &P, PDB_ColorItem C) : OS(P.OS) { - if (C == PDB_ColorItem::None) - OS.resetColor(); - else { - raw_ostream::Colors Color; - bool Bold; - translateColor(C, Color, Bold); - OS.changeColor(Color, Bold); - } + applyColor(C); } WithColor::~WithColor() { OS.resetColor(); } -void WithColor::translateColor(PDB_ColorItem C, raw_ostream::Colors &Color, - bool &Bold) const { +void WithColor::applyColor(PDB_ColorItem C) { switch (C) { + case PDB_ColorItem::None: + OS.resetColor(); + return; case PDB_ColorItem::Address: - Color = raw_ostream::YELLOW; - Bold = true; + OS.changeColor(raw_ostream::YELLOW, /*bold=*/true); return; case PDB_ColorItem::Keyword: - Color = raw_ostream::MAGENTA; - Bold = true; + OS.changeColor(raw_ostream::MAGENTA, true); return; case PDB_ColorItem::Register: case PDB_ColorItem::Offset: - Color = raw_ostream::YELLOW; - Bold = false; + OS.changeColor(raw_ostream::YELLOW, false); return; case PDB_ColorItem::Type: - Color = raw_ostream::CYAN; - Bold = true; + OS.changeColor(raw_ostream::CYAN, true); return; case PDB_ColorItem::Identifier: - Color = raw_ostream::CYAN; - Bold = false; + OS.changeColor(raw_ostream::CYAN, false); return; case PDB_ColorItem::Path: - Color = raw_ostream::CYAN; - Bold = false; + OS.changeColor(raw_ostream::CYAN, false); return; case PDB_ColorItem::SectionHeader: - Color = raw_ostream::RED; - Bold = true; + OS.changeColor(raw_ostream::RED, true); return; case PDB_ColorItem::LiteralValue: - Color = raw_ostream::GREEN; - Bold = true; - default: + OS.changeColor(raw_ostream::GREEN, true); return; } } diff --git a/tools/llvm-pdbdump/LinePrinter.h b/tools/llvm-pdbdump/LinePrinter.h index b985e9341909..b0a9d2cfc4e8 100644 --- a/tools/llvm-pdbdump/LinePrinter.h +++ b/tools/llvm-pdbdump/LinePrinter.h @@ -48,9 +48,13 @@ private: int IndentSpaces; int CurrentIndent; - std::list<Regex> CompilandFilters; - std::list<Regex> TypeFilters; - std::list<Regex> SymbolFilters; + std::list<Regex> ExcludeCompilandFilters; + std::list<Regex> ExcludeTypeFilters; + std::list<Regex> ExcludeSymbolFilters; + + std::list<Regex> IncludeCompilandFilters; + std::list<Regex> IncludeTypeFilters; + std::list<Regex> IncludeSymbolFilters; }; template <class T> @@ -80,8 +84,7 @@ public: raw_ostream &get() { return OS; } private: - void translateColor(PDB_ColorItem C, raw_ostream::Colors &Color, - bool &Bold) const; + void applyColor(PDB_ColorItem C); raw_ostream &OS; }; } diff --git a/tools/llvm-pdbdump/llvm-pdbdump.cpp b/tools/llvm-pdbdump/llvm-pdbdump.cpp index 4a4c64b80cc1..0e3f0b281fe0 100644 --- a/tools/llvm-pdbdump/llvm-pdbdump.cpp +++ b/tools/llvm-pdbdump/llvm-pdbdump.cpp @@ -22,6 +22,8 @@ #include "VariableDumper.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Config/config.h" #include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" @@ -38,12 +40,16 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" #include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Process.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/Signals.h" #if defined(HAVE_DIA_SDK) +#ifndef NOMINMAX +#define NOMINMAX +#endif #include <Windows.h> #endif @@ -79,6 +85,17 @@ cl::opt<uint64_t> LoadAddress( cl::desc("Assume the module is loaded at the specified address"), cl::cat(OtherOptions)); +cl::opt<bool> DumpHeaders("dump-headers", cl::desc("dump PDB headers"), + cl::cat(OtherOptions)); +cl::opt<bool> DumpStreamSizes("dump-stream-sizes", + cl::desc("dump PDB stream sizes"), + cl::cat(OtherOptions)); +cl::opt<bool> DumpStreamBlocks("dump-stream-blocks", + cl::desc("dump PDB stream blocks"), + cl::cat(OtherOptions)); +cl::opt<std::string> DumpStreamData("dump-stream", cl::desc("dump stream data"), + cl::cat(OtherOptions)); + cl::list<std::string> ExcludeTypes("exclude-types", cl::desc("Exclude types by regular expression"), @@ -91,6 +108,20 @@ cl::list<std::string> ExcludeCompilands("exclude-compilands", cl::desc("Exclude compilands by regular expression"), cl::ZeroOrMore, cl::cat(FilterCategory)); + +cl::list<std::string> IncludeTypes( + "include-types", + cl::desc("Include only types which match a regular expression"), + cl::ZeroOrMore, cl::cat(FilterCategory)); +cl::list<std::string> IncludeSymbols( + "include-symbols", + cl::desc("Include only symbols which match a regular expression"), + cl::ZeroOrMore, cl::cat(FilterCategory)); +cl::list<std::string> IncludeCompilands( + "include-compilands", + cl::desc("Include only compilands those which match a regular expression"), + cl::ZeroOrMore, cl::cat(FilterCategory)); + cl::opt<bool> ExcludeCompilerGenerated( "no-compiler-generated", cl::desc("Don't show compiler generated types and symbols"), @@ -107,10 +138,264 @@ cl::opt<bool> NoEnumDefs("no-enum-definitions", cl::cat(FilterCategory)); } + +static void reportError(StringRef Input, StringRef Message) { + if (Input == "-") + Input = "<stdin>"; + errs() << Input << ": " << Message << "\n"; + errs().flush(); + exit(1); +} + +static void reportError(StringRef Input, std::error_code EC) { + reportError(Input, EC.message()); +} + +static std::error_code checkOffset(MemoryBufferRef M, uintptr_t Addr, + const uint64_t Size) { + if (Addr + Size < Addr || Addr + Size < Size || + Addr + Size > uintptr_t(M.getBufferEnd()) || + Addr < uintptr_t(M.getBufferStart())) { + return std::make_error_code(std::errc::bad_address); + } + return std::error_code(); +} + +template <typename T> +static std::error_code checkOffset(MemoryBufferRef M, ArrayRef<T> AR) { + return checkOffset(M, uintptr_t(AR.data()), (uint64_t)AR.size() * sizeof(T)); +} + +static std::error_code checkOffset(MemoryBufferRef M, StringRef SR) { + return checkOffset(M, uintptr_t(SR.data()), SR.size()); +} + +// Sets Obj unless any bytes in [addr, addr + size) fall outsize of m. +// Returns unexpected_eof if error. +template <typename T> +static std::error_code getObject(const T *&Obj, MemoryBufferRef M, + const void *Ptr, + const uint64_t Size = sizeof(T)) { + uintptr_t Addr = uintptr_t(Ptr); + if (std::error_code EC = checkOffset(M, Addr, Size)) + return EC; + Obj = reinterpret_cast<const T *>(Addr); + return std::error_code(); +} + +static uint64_t bytesToBlocks(uint64_t NumBytes, uint64_t BlockSize) { + return RoundUpToAlignment(NumBytes, BlockSize) / BlockSize; +} + +static uint64_t blockToOffset(uint64_t BlockNumber, uint64_t BlockSize) { + return BlockNumber * BlockSize; +} + +static void dumpStructure(MemoryBufferRef M) { + const PDB::SuperBlock *SB; + if (auto EC = getObject(SB, M, M.getBufferStart())) + reportError(M.getBufferIdentifier(), EC); + + if (opts::DumpHeaders) { + outs() << "BlockSize: " << SB->BlockSize << '\n'; + outs() << "Unknown0: " << SB->Unknown0 << '\n'; + outs() << "NumBlocks: " << SB->NumBlocks << '\n'; + outs() << "NumDirectoryBytes: " << SB->NumDirectoryBytes << '\n'; + outs() << "Unknown1: " << SB->Unknown1 << '\n'; + outs() << "BlockMapAddr: " << SB->BlockMapAddr << '\n'; + } + + // We don't support blocksizes which aren't a multiple of four bytes. + if (SB->BlockSize % sizeof(support::ulittle32_t) != 0) + reportError(M.getBufferIdentifier(), + std::make_error_code(std::errc::illegal_byte_sequence)); + + // We don't support directories whose sizes aren't a multiple of four bytes. + if (SB->NumDirectoryBytes % sizeof(support::ulittle32_t) != 0) + reportError(M.getBufferIdentifier(), + std::make_error_code(std::errc::illegal_byte_sequence)); + + // The number of blocks which comprise the directory is a simple function of + // the number of bytes it contains. + uint64_t NumDirectoryBlocks = + bytesToBlocks(SB->NumDirectoryBytes, SB->BlockSize); + if (opts::DumpHeaders) + outs() << "NumDirectoryBlocks: " << NumDirectoryBlocks << '\n'; + + // The block map, as we understand it, is a block which consists of a list of + // block numbers. + // It is unclear what would happen if the number of blocks couldn't fit on a + // single block. + if (NumDirectoryBlocks > SB->BlockSize / sizeof(support::ulittle32_t)) + reportError(M.getBufferIdentifier(), + std::make_error_code(std::errc::illegal_byte_sequence)); + + + uint64_t BlockMapOffset = (uint64_t)SB->BlockMapAddr * SB->BlockSize; + if (opts::DumpHeaders) + outs() << "BlockMapOffset: " << BlockMapOffset << '\n'; + + // The directory is not contiguous. Instead, the block map contains a + // contiguous list of block numbers whose contents, when concatenated in + // order, make up the directory. + auto DirectoryBlocks = + makeArrayRef(reinterpret_cast<const support::ulittle32_t *>( + M.getBufferStart() + BlockMapOffset), + NumDirectoryBlocks); + if (auto EC = checkOffset(M, DirectoryBlocks)) + reportError(M.getBufferIdentifier(), EC); + + if (opts::DumpHeaders) { + outs() << "DirectoryBlocks: ["; + for (const support::ulittle32_t &DirectoryBlockAddr : DirectoryBlocks) { + if (&DirectoryBlockAddr != &DirectoryBlocks.front()) + outs() << ", "; + outs() << DirectoryBlockAddr; + } + outs() << "]\n"; + } + + bool SeenNumStreams = false; + uint32_t NumStreams = 0; + std::vector<uint32_t> StreamSizes; + DenseMap<uint32_t, std::vector<uint32_t>> StreamMap; + uint32_t StreamIdx = 0; + uint64_t DirectoryBytesRead = 0; + // The structure of the directory is as follows: + // struct PDBDirectory { + // uint32_t NumStreams; + // uint32_t StreamSizes[NumStreams]; + // uint32_t StreamMap[NumStreams][]; + // }; + // + // Empty streams don't consume entries in the StreamMap. + for (uint32_t DirectoryBlockAddr : DirectoryBlocks) { + uint64_t DirectoryBlockOffset = + blockToOffset(DirectoryBlockAddr, SB->BlockSize); + auto DirectoryBlock = + makeArrayRef(reinterpret_cast<const support::ulittle32_t *>( + M.getBufferStart() + DirectoryBlockOffset), + SB->BlockSize / sizeof(support::ulittle32_t)); + if (auto EC = checkOffset(M, DirectoryBlock)) + reportError(M.getBufferIdentifier(), EC); + + // We read data out of the directory four bytes at a time. Depending on + // where we are in the directory, the contents may be: the number of streams + // in the directory, a stream's size, or a block in the stream map. + for (uint32_t Data : DirectoryBlock) { + // Don't read beyond the end of the directory. + if (DirectoryBytesRead == SB->NumDirectoryBytes) + break; + + DirectoryBytesRead += sizeof(Data); + + // This data must be the number of streams if we haven't seen it yet. + if (!SeenNumStreams) { + NumStreams = Data; + SeenNumStreams = true; + continue; + } + // This data must be a stream size if we have not seen them all yet. + if (StreamSizes.size() < NumStreams) { + // It seems like some streams have their set to -1 when their contents + // are not present. Treat them like empty streams for now. + if (Data == UINT32_MAX) + StreamSizes.push_back(0); + else + StreamSizes.push_back(Data); + continue; + } + + // This data must be a stream block number if we have seen all of the + // stream sizes. + std::vector<uint32_t> *StreamBlocks = nullptr; + // Figure out which stream this block number belongs to. + while (StreamIdx < NumStreams) { + uint64_t NumExpectedStreamBlocks = + bytesToBlocks(StreamSizes[StreamIdx], SB->BlockSize); + StreamBlocks = &StreamMap[StreamIdx]; + if (NumExpectedStreamBlocks > StreamBlocks->size()) + break; + ++StreamIdx; + } + // It seems this block doesn't belong to any stream? The stream is either + // corrupt or something more mysterious is going on. + if (StreamIdx == NumStreams) + reportError(M.getBufferIdentifier(), + std::make_error_code(std::errc::illegal_byte_sequence)); + + StreamBlocks->push_back(Data); + } + } + + // We should have read exactly SB->NumDirectoryBytes bytes. + assert(DirectoryBytesRead == SB->NumDirectoryBytes); + + if (opts::DumpHeaders) + outs() << "NumStreams: " << NumStreams << '\n'; + if (opts::DumpStreamSizes) + for (uint32_t StreamIdx = 0; StreamIdx < NumStreams; ++StreamIdx) + outs() << "StreamSizes[" << StreamIdx << "]: " << StreamSizes[StreamIdx] + << '\n'; + + if (opts::DumpStreamBlocks) { + for (uint32_t StreamIdx = 0; StreamIdx < NumStreams; ++StreamIdx) { + outs() << "StreamBlocks[" << StreamIdx << "]: ["; + std::vector<uint32_t> &StreamBlocks = StreamMap[StreamIdx]; + for (uint32_t &StreamBlock : StreamBlocks) { + if (&StreamBlock != &StreamBlocks.front()) + outs() << ", "; + outs() << StreamBlock; + } + outs() << "]\n"; + } + } + + StringRef DumpStreamStr = opts::DumpStreamData; + uint32_t DumpStreamNum; + if (!DumpStreamStr.getAsInteger(/*Radix=*/0U, DumpStreamNum) && + DumpStreamNum < NumStreams) { + uint32_t StreamBytesRead = 0; + uint32_t StreamSize = StreamSizes[DumpStreamNum]; + std::vector<uint32_t> &StreamBlocks = StreamMap[DumpStreamNum]; + for (uint32_t &StreamBlockAddr : StreamBlocks) { + uint64_t StreamBlockOffset = blockToOffset(StreamBlockAddr, SB->BlockSize); + uint32_t BytesLeftToReadInStream = StreamSize - StreamBytesRead; + if (BytesLeftToReadInStream == 0) + break; + + uint32_t BytesToReadInBlock = std::min( + BytesLeftToReadInStream, static_cast<uint32_t>(SB->BlockSize)); + auto StreamBlockData = + StringRef(M.getBufferStart() + StreamBlockOffset, BytesToReadInBlock); + if (auto EC = checkOffset(M, StreamBlockData)) + reportError(M.getBufferIdentifier(), EC); + + outs() << StreamBlockData; + StreamBytesRead += StreamBlockData.size(); + } + } +} + static void dumpInput(StringRef Path) { + if (opts::DumpHeaders || !opts::DumpStreamData.empty()) { + ErrorOr<std::unique_ptr<MemoryBuffer>> ErrorOrBuffer = + MemoryBuffer::getFileOrSTDIN(Path, /*FileSize=*/-1, + /*RequiresNullTerminator=*/false); + + if (std::error_code EC = ErrorOrBuffer.getError()) + reportError(Path, EC); + + std::unique_ptr<MemoryBuffer> &Buffer = ErrorOrBuffer.get(); + + dumpStructure(Buffer->getMemBufferRef()); + + outs().flush(); + return; + } + std::unique_ptr<IPDBSession> Session; - PDB_ErrorCode Error = - llvm::loadDataForPDB(PDB_ReaderType::DIA, Path, Session); + PDB_ErrorCode Error = loadDataForPDB(PDB_ReaderType::DIA, Path, Session); switch (Error) { case PDB_ErrorCode::Success: break; @@ -145,7 +430,7 @@ static void dumpInput(StringRef Path) { Printer.NewLine(); WithColor(Printer, PDB_ColorItem::Identifier).get() << "Size"; - if (!llvm::sys::fs::file_size(FileName, FileSize)) { + if (!sys::fs::file_size(FileName, FileSize)) { Printer << ": " << FileSize << " bytes"; } else { Printer << ": (Unable to obtain file size)"; @@ -242,11 +527,11 @@ int main(int argc_, const char *argv_[]) { PrettyStackTraceProgram X(argc_, argv_); SmallVector<const char *, 256> argv; - llvm::SpecificBumpPtrAllocator<char> ArgAllocator; - std::error_code EC = llvm::sys::Process::GetArgumentVector( - argv, llvm::makeArrayRef(argv_, argc_), ArgAllocator); + SpecificBumpPtrAllocator<char> ArgAllocator; + std::error_code EC = sys::Process::GetArgumentVector( + argv, makeArrayRef(argv_, argc_), ArgAllocator); if (EC) { - llvm::errs() << "error: couldn't get arguments: " << EC.message() << '\n'; + errs() << "error: couldn't get arguments: " << EC.message() << '\n'; return 1; } diff --git a/tools/llvm-pdbdump/llvm-pdbdump.h b/tools/llvm-pdbdump/llvm-pdbdump.h index 586a9ea374ed..cb5bec64dec8 100644 --- a/tools/llvm-pdbdump/llvm-pdbdump.h +++ b/tools/llvm-pdbdump/llvm-pdbdump.h @@ -27,6 +27,9 @@ extern llvm::cl::opt<bool> NoEnumDefs; extern llvm::cl::list<std::string> ExcludeTypes; extern llvm::cl::list<std::string> ExcludeSymbols; extern llvm::cl::list<std::string> ExcludeCompilands; +extern llvm::cl::list<std::string> IncludeTypes; +extern llvm::cl::list<std::string> IncludeSymbols; +extern llvm::cl::list<std::string> IncludeCompilands; } #endif
\ No newline at end of file diff --git a/tools/llvm-profdata/llvm-profdata.cpp b/tools/llvm-profdata/llvm-profdata.cpp index 6fb48d8fad58..dc6cd0a5784d 100644 --- a/tools/llvm-profdata/llvm-profdata.cpp +++ b/tools/llvm-profdata/llvm-profdata.cpp @@ -11,6 +11,8 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/IR/LLVMContext.h" #include "llvm/ProfileData/InstrProfReader.h" @@ -18,6 +20,7 @@ #include "llvm/ProfileData/SampleProfReader.h" #include "llvm/ProfileData/SampleProfWriter.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/Errc.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" #include "llvm/Support/ManagedStatic.h" @@ -26,67 +29,150 @@ #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <tuple> using namespace llvm; -static void exitWithError(const Twine &Message, StringRef Whence = "") { +enum ProfileFormat { PF_None = 0, PF_Text, PF_Binary, PF_GCC }; + +static void exitWithError(const Twine &Message, StringRef Whence = "", + StringRef Hint = "") { errs() << "error: "; if (!Whence.empty()) errs() << Whence << ": "; errs() << Message << "\n"; + if (!Hint.empty()) + errs() << Hint << "\n"; ::exit(1); } +static void exitWithErrorCode(const std::error_code &Error, + StringRef Whence = "") { + if (Error.category() == instrprof_category()) { + instrprof_error instrError = static_cast<instrprof_error>(Error.value()); + if (instrError == instrprof_error::unrecognized_format) { + // Hint for common error of forgetting -sample for sample profiles. + exitWithError(Error.message(), Whence, + "Perhaps you forgot to use the -sample option?"); + } + } + exitWithError(Error.message(), Whence); +} + namespace { enum ProfileKinds { instr, sample }; } -static void mergeInstrProfile(const cl::list<std::string> &Inputs, - StringRef OutputFilename) { +static void handleMergeWriterError(std::error_code &Error, + StringRef WhenceFile = "", + StringRef WhenceFunction = "", + bool ShowHint = true) { + if (!WhenceFile.empty()) + errs() << WhenceFile << ": "; + if (!WhenceFunction.empty()) + errs() << WhenceFunction << ": "; + errs() << Error.message() << "\n"; + + if (ShowHint) { + StringRef Hint = ""; + if (Error.category() == instrprof_category()) { + instrprof_error instrError = static_cast<instrprof_error>(Error.value()); + switch (instrError) { + case instrprof_error::hash_mismatch: + case instrprof_error::count_mismatch: + case instrprof_error::value_site_count_mismatch: + Hint = "Make sure that all profile data to be merged is generated " + "from the same binary."; + break; + default: + break; + } + } + + if (!Hint.empty()) + errs() << Hint << "\n"; + } +} + +struct WeightedFile { + StringRef Filename; + uint64_t Weight; + + WeightedFile() {} + + WeightedFile(StringRef F, uint64_t W) : Filename{F}, Weight{W} {} +}; +typedef SmallVector<WeightedFile, 5> WeightedFileVector; + +static void mergeInstrProfile(const WeightedFileVector &Inputs, + StringRef OutputFilename, + ProfileFormat OutputFormat) { if (OutputFilename.compare("-") == 0) exitWithError("Cannot write indexed profdata format to stdout."); + if (OutputFormat != PF_Binary && OutputFormat != PF_Text) + exitWithError("Unknown format is specified."); + std::error_code EC; raw_fd_ostream Output(OutputFilename.data(), EC, sys::fs::F_None); if (EC) - exitWithError(EC.message(), OutputFilename); + exitWithErrorCode(EC, OutputFilename); InstrProfWriter Writer; - for (const auto &Filename : Inputs) { - auto ReaderOrErr = InstrProfReader::create(Filename); + SmallSet<std::error_code, 4> WriterErrorCodes; + for (const auto &Input : Inputs) { + auto ReaderOrErr = InstrProfReader::create(Input.Filename); if (std::error_code ec = ReaderOrErr.getError()) - exitWithError(ec.message(), Filename); + exitWithErrorCode(ec, Input.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)) - errs() << Filename << ": " << I.Name << ": " << EC.message() << "\n"; + for (auto &I : *Reader) { + if (std::error_code EC = Writer.addRecord(std::move(I), Input.Weight)) { + // Only show hint the first time an error occurs. + bool firstTime = WriterErrorCodes.insert(EC).second; + handleMergeWriterError(EC, Input.Filename, I.Name, firstTime); + } + } if (Reader->hasError()) - exitWithError(Reader->getError().message(), Filename); + exitWithErrorCode(Reader->getError(), Input.Filename); } - Writer.write(Output); + if (OutputFormat == PF_Text) + Writer.writeText(Output); + else + Writer.write(Output); } -static void mergeSampleProfile(const cl::list<std::string> &Inputs, +static sampleprof::SampleProfileFormat FormatMap[] = { + sampleprof::SPF_None, sampleprof::SPF_Text, sampleprof::SPF_Binary, + sampleprof::SPF_GCC}; + +static void mergeSampleProfile(const WeightedFileVector &Inputs, StringRef OutputFilename, - sampleprof::SampleProfileFormat OutputFormat) { + ProfileFormat OutputFormat) { using namespace sampleprof; - auto WriterOrErr = SampleProfileWriter::create(OutputFilename, OutputFormat); + auto WriterOrErr = + SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]); if (std::error_code EC = WriterOrErr.getError()) - exitWithError(EC.message(), OutputFilename); + exitWithErrorCode(EC, OutputFilename); auto Writer = std::move(WriterOrErr.get()); StringMap<FunctionSamples> ProfileMap; - for (const auto &Filename : Inputs) { + SmallVector<std::unique_ptr<sampleprof::SampleProfileReader>, 5> Readers; + for (const auto &Input : Inputs) { auto ReaderOrErr = - SampleProfileReader::create(Filename, getGlobalContext()); + SampleProfileReader::create(Input.Filename, getGlobalContext()); if (std::error_code EC = ReaderOrErr.getError()) - exitWithError(EC.message(), Filename); + exitWithErrorCode(EC, Input.Filename); - auto Reader = std::move(ReaderOrErr.get()); + // We need to keep the readers around until after all the files are + // read so that we do not lose the function names stored in each + // reader's memory. The function names are needed to write out the + // merged profile map. + Readers.push_back(std::move(ReaderOrErr.get())); + const auto Reader = Readers.back().get(); if (std::error_code EC = Reader->read()) - exitWithError(EC.message(), Filename); + exitWithErrorCode(EC, Input.Filename); StringMap<FunctionSamples> &Profiles = Reader->getProfiles(); for (StringMap<FunctionSamples>::iterator I = Profiles.begin(), @@ -94,16 +180,36 @@ static void mergeSampleProfile(const cl::list<std::string> &Inputs, I != E; ++I) { StringRef FName = I->first(); FunctionSamples &Samples = I->second; - ProfileMap[FName].merge(Samples); + sampleprof_error Result = ProfileMap[FName].merge(Samples, Input.Weight); + if (Result != sampleprof_error::success) { + std::error_code EC = make_error_code(Result); + handleMergeWriterError(EC, Input.Filename, FName); + } } } Writer->write(ProfileMap); } -static int merge_main(int argc, const char *argv[]) { - cl::list<std::string> Inputs(cl::Positional, cl::Required, cl::OneOrMore, - cl::desc("<filenames...>")); +static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) { + StringRef WeightStr, FileName; + std::tie(WeightStr, FileName) = WeightedFilename.split(','); + + uint64_t Weight; + if (WeightStr.getAsInteger(10, Weight) || Weight < 1) + exitWithError("Input weight must be a positive integer."); + + if (!sys::fs::exists(FileName)) + exitWithErrorCode(make_error_code(errc::no_such_file_or_directory), + FileName); + + return WeightedFile(FileName, Weight); +} +static int merge_main(int argc, const char *argv[]) { + cl::list<std::string> InputFilenames(cl::Positional, + cl::desc("<filename...>")); + cl::list<std::string> WeightedInputFilenames("weighted-input", + cl::desc("<weight>,<filename>")); cl::opt<std::string> OutputFilename("output", cl::value_desc("output"), cl::init("-"), cl::Required, cl::desc("Output file")); @@ -114,31 +220,41 @@ static int merge_main(int argc, const char *argv[]) { 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"), + cl::opt<ProfileFormat> OutputFormat( + cl::desc("Format of output profile"), cl::init(PF_Binary), + cl::values(clEnumValN(PF_Binary, "binary", "Binary encoding (default)"), + clEnumValN(PF_Text, "text", "Text encoding"), + clEnumValN(PF_GCC, "gcc", + "GCC encoding (only meaningful for -sample)"), clEnumValEnd)); cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); + if (InputFilenames.empty() && WeightedInputFilenames.empty()) + exitWithError("No input files specified. See " + + sys::path::filename(argv[0]) + " -help"); + + WeightedFileVector WeightedInputs; + for (StringRef Filename : InputFilenames) + WeightedInputs.push_back(WeightedFile(Filename, 1)); + for (StringRef WeightedFilename : WeightedInputFilenames) + WeightedInputs.push_back(parseWeightedFile(WeightedFilename)); + if (ProfileKind == instr) - mergeInstrProfile(Inputs, OutputFilename); + mergeInstrProfile(WeightedInputs, OutputFilename, OutputFormat); else - mergeSampleProfile(Inputs, OutputFilename, OutputFormat); + mergeSampleProfile(WeightedInputs, OutputFilename, OutputFormat); return 0; } static int showInstrProfile(std::string Filename, bool ShowCounts, - bool ShowAllFunctions, std::string ShowFunction, + bool ShowIndirectCallTargets, bool ShowAllFunctions, + std::string ShowFunction, bool TextFormat, raw_fd_ostream &OS) { auto ReaderOrErr = InstrProfReader::create(Filename); if (std::error_code EC = ReaderOrErr.getError()) - exitWithError(EC.message(), Filename); + exitWithErrorCode(EC, Filename); auto Reader = std::move(ReaderOrErr.get()); uint64_t MaxFunctionCount = 0, MaxBlockCount = 0; @@ -148,35 +264,71 @@ static int showInstrProfile(std::string Filename, bool ShowCounts, ShowAllFunctions || (!ShowFunction.empty() && Func.Name.find(ShowFunction) != Func.Name.npos); + bool doTextFormatDump = (Show && ShowCounts && TextFormat); + + if (doTextFormatDump) { + InstrProfSymtab &Symtab = Reader->getSymtab(); + InstrProfWriter::writeRecordInText(Func, Symtab, OS); + continue; + } + ++TotalFunctions; assert(Func.Counts.size() > 0 && "function missing entry counter"); if (Func.Counts[0] > MaxFunctionCount) MaxFunctionCount = Func.Counts[0]; + for (size_t I = 1, E = Func.Counts.size(); I < E; ++I) { + if (Func.Counts[I] > MaxBlockCount) + MaxBlockCount = Func.Counts[I]; + } + if (Show) { + if (!ShownFunctions) OS << "Counters:\n"; + ++ShownFunctions; OS << " " << Func.Name << ":\n" << " Hash: " << format("0x%016" PRIx64, Func.Hash) << "\n" << " Counters: " << Func.Counts.size() << "\n" << " Function count: " << Func.Counts[0] << "\n"; - } - if (Show && ShowCounts) - OS << " Block counts: ["; - for (size_t I = 1, E = Func.Counts.size(); I < E; ++I) { - if (Func.Counts[I] > MaxBlockCount) - MaxBlockCount = Func.Counts[I]; - if (Show && ShowCounts) - OS << (I == 1 ? "" : ", ") << Func.Counts[I]; + if (ShowIndirectCallTargets) + OS << " Indirect Call Site Count: " + << Func.getNumValueSites(IPVK_IndirectCallTarget) << "\n"; + + if (ShowCounts) { + OS << " Block counts: ["; + for (size_t I = 1, E = Func.Counts.size(); I < E; ++I) { + OS << (I == 1 ? "" : ", ") << Func.Counts[I]; + } + OS << "]\n"; + } + + if (ShowIndirectCallTargets) { + InstrProfSymtab &Symtab = Reader->getSymtab(); + uint32_t NS = Func.getNumValueSites(IPVK_IndirectCallTarget); + OS << " Indirect Target Results: \n"; + for (size_t I = 0; I < NS; ++I) { + uint32_t NV = Func.getNumValueDataForSite(IPVK_IndirectCallTarget, I); + std::unique_ptr<InstrProfValueData[]> VD = + Func.getValueForSite(IPVK_IndirectCallTarget, I); + for (uint32_t V = 0; V < NV; V++) { + OS << "\t[ " << I << ", "; + OS << Symtab.getFuncName(VD[V].Value) << ", " << VD[V].Count + << " ]\n"; + } + } + } } - if (Show && ShowCounts) - OS << "]\n"; } + if (Reader->hasError()) - exitWithError(Reader->getError().message(), Filename); + exitWithErrorCode(Reader->getError(), Filename); + + if (ShowCounts && TextFormat) + return 0; if (ShowAllFunctions || !ShowFunction.empty()) OS << "Functions shown: " << ShownFunctions << "\n"; @@ -192,10 +344,12 @@ static int showSampleProfile(std::string Filename, bool ShowCounts, using namespace sampleprof; auto ReaderOrErr = SampleProfileReader::create(Filename, getGlobalContext()); if (std::error_code EC = ReaderOrErr.getError()) - exitWithError(EC.message(), Filename); + exitWithErrorCode(EC, Filename); auto Reader = std::move(ReaderOrErr.get()); - Reader->read(); + if (std::error_code EC = Reader->read()) + exitWithErrorCode(EC, Filename); + if (ShowAllFunctions || ShowFunction.empty()) Reader->dump(OS); else @@ -210,6 +364,12 @@ static int show_main(int argc, const char *argv[]) { cl::opt<bool> ShowCounts("counts", cl::init(false), cl::desc("Show counter values for shown functions")); + cl::opt<bool> TextFormat( + "text", cl::init(false), + cl::desc("Show instr profile data in text dump format")); + cl::opt<bool> ShowIndirectCallTargets( + "ic-targets", cl::init(false), + cl::desc("Show indirect call site target 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", @@ -232,14 +392,14 @@ static int show_main(int argc, const char *argv[]) { std::error_code EC; raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::F_Text); if (EC) - exitWithError(EC.message(), OutputFilename); + exitWithErrorCode(EC, OutputFilename); if (ShowAllFunctions && !ShowFunction.empty()) errs() << "warning: -function argument ignored: showing all functions\n"; if (ProfileKind == instr) - return showInstrProfile(Filename, ShowCounts, ShowAllFunctions, - ShowFunction, OS); + return showInstrProfile(Filename, ShowCounts, ShowIndirectCallTargets, + ShowAllFunctions, ShowFunction, TextFormat, OS); else return showSampleProfile(Filename, ShowCounts, ShowAllFunctions, ShowFunction, OS); @@ -266,8 +426,7 @@ int main(int argc, const char *argv[]) { return func(argc - 1, argv + 1); } - if (strcmp(argv[1], "-h") == 0 || - strcmp(argv[1], "-help") == 0 || + if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-help") == 0 || strcmp(argv[1], "--help") == 0) { errs() << "OVERVIEW: LLVM profile data tools\n\n" diff --git a/tools/llvm-readobj/ARMAttributeParser.cpp b/tools/llvm-readobj/ARMAttributeParser.cpp index e2d71912a21e..688d349d7ec0 100644 --- a/tools/llvm-readobj/ARMAttributeParser.cpp +++ b/tools/llvm-readobj/ARMAttributeParser.cpp @@ -118,7 +118,7 @@ void ARMAttributeParser::PrintAttribute(unsigned Tag, unsigned Value, void ARMAttributeParser::CPU_arch(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { + static const char *const Strings[] = { "Pre-v4", "ARM v4", "ARM v4T", "ARM v5T", "ARM v5TE", "ARM v5TEJ", "ARM v6", "ARM v6KZ", "ARM v6T2", "ARM v6K", "ARM v7", "ARM v6-M", "ARM v6S-M", "ARM v7E-M", "ARM v8" @@ -149,7 +149,7 @@ void ARMAttributeParser::CPU_arch_profile(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::ARM_ISA_use(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { "Not Permitted", "Permitted" }; + static const char *const Strings[] = { "Not Permitted", "Permitted" }; uint64_t Value = ParseInteger(Data, Offset); StringRef ValueDesc = @@ -159,7 +159,7 @@ void ARMAttributeParser::ARM_ISA_use(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::THUMB_ISA_use(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { "Not Permitted", "Thumb-1", "Thumb-2" }; + static const char *const Strings[] = { "Not Permitted", "Thumb-1", "Thumb-2" }; uint64_t Value = ParseInteger(Data, Offset); StringRef ValueDesc = @@ -169,7 +169,7 @@ void ARMAttributeParser::THUMB_ISA_use(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::FP_arch(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { + static const char *const Strings[] = { "Not Permitted", "VFPv1", "VFPv2", "VFPv3", "VFPv3-D16", "VFPv4", "VFPv4-D16", "ARMv8-a FP", "ARMv8-a FP-D16" }; @@ -182,7 +182,7 @@ void ARMAttributeParser::FP_arch(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::WMMX_arch(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { "Not Permitted", "WMMXv1", "WMMXv2" }; + static const char *const Strings[] = { "Not Permitted", "WMMXv1", "WMMXv2" }; uint64_t Value = ParseInteger(Data, Offset); StringRef ValueDesc = @@ -192,8 +192,8 @@ void ARMAttributeParser::WMMX_arch(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::Advanced_SIMD_arch(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { - "Not Permitted", "NEONv1", "NEONv2+FMA", "ARMv8-a NEON" + static const char *const Strings[] = { + "Not Permitted", "NEONv1", "NEONv2+FMA", "ARMv8-a NEON", "ARMv8.1-a NEON" }; uint64_t Value = ParseInteger(Data, Offset); @@ -204,7 +204,7 @@ void ARMAttributeParser::Advanced_SIMD_arch(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::PCS_config(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { + static const char *const Strings[] = { "None", "Bare Platform", "Linux Application", "Linux DSO", "Palm OS 2004", "Reserved (Palm OS)", "Symbian OS 2004", "Reserved (Symbian OS)" }; @@ -217,7 +217,7 @@ void ARMAttributeParser::PCS_config(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::ABI_PCS_R9_use(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { "v6", "Static Base", "TLS", "Unused" }; + static const char *const Strings[] = { "v6", "Static Base", "TLS", "Unused" }; uint64_t Value = ParseInteger(Data, Offset); StringRef ValueDesc = @@ -227,7 +227,7 @@ void ARMAttributeParser::ABI_PCS_R9_use(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::ABI_PCS_RW_data(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { + static const char *const Strings[] = { "Absolute", "PC-relative", "SB-relative", "Not Permitted" }; @@ -239,7 +239,9 @@ void ARMAttributeParser::ABI_PCS_RW_data(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::ABI_PCS_RO_data(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { "Absolute", "PC-relative", "Not Permitted" }; + static const char *const Strings[] = { + "Absolute", "PC-relative", "Not Permitted" + }; uint64_t Value = ParseInteger(Data, Offset); StringRef ValueDesc = @@ -249,7 +251,9 @@ void ARMAttributeParser::ABI_PCS_RO_data(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::ABI_PCS_GOT_use(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { "Not Permitted", "Direct", "GOT-Indirect" }; + static const char *const Strings[] = { + "Not Permitted", "Direct", "GOT-Indirect" + }; uint64_t Value = ParseInteger(Data, Offset); StringRef ValueDesc = @@ -259,7 +263,7 @@ void ARMAttributeParser::ABI_PCS_GOT_use(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::ABI_PCS_wchar_t(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { + static const char *const Strings[] = { "Not Permitted", "Unknown", "2-byte", "Unknown", "4-byte" }; @@ -271,7 +275,7 @@ void ARMAttributeParser::ABI_PCS_wchar_t(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::ABI_FP_rounding(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { "IEEE-754", "Runtime" }; + static const char *const Strings[] = { "IEEE-754", "Runtime" }; uint64_t Value = ParseInteger(Data, Offset); StringRef ValueDesc = @@ -281,7 +285,9 @@ void ARMAttributeParser::ABI_FP_rounding(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::ABI_FP_denormal(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { "Unsupported", "IEEE-754", "Sign Only" }; + static const char *const Strings[] = { + "Unsupported", "IEEE-754", "Sign Only" + }; uint64_t Value = ParseInteger(Data, Offset); StringRef ValueDesc = @@ -291,7 +297,7 @@ void ARMAttributeParser::ABI_FP_denormal(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::ABI_FP_exceptions(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { "Not Permitted", "IEEE-754" }; + static const char *const Strings[] = { "Not Permitted", "IEEE-754" }; uint64_t Value = ParseInteger(Data, Offset); StringRef ValueDesc = @@ -302,7 +308,7 @@ void ARMAttributeParser::ABI_FP_exceptions(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::ABI_FP_user_exceptions(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { "Not Permitted", "IEEE-754" }; + static const char *const Strings[] = { "Not Permitted", "IEEE-754" }; uint64_t Value = ParseInteger(Data, Offset); StringRef ValueDesc = @@ -312,7 +318,7 @@ void ARMAttributeParser::ABI_FP_user_exceptions(AttrType Tag, void ARMAttributeParser::ABI_FP_number_model(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { + static const char *const Strings[] = { "Not Permitted", "Finite Only", "RTABI", "IEEE-754" }; @@ -324,7 +330,7 @@ void ARMAttributeParser::ABI_FP_number_model(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::ABI_align_needed(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { + static const char *const Strings[] = { "Not Permitted", "8-byte alignment", "4-byte alignment", "Reserved" }; @@ -344,7 +350,7 @@ void ARMAttributeParser::ABI_align_needed(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::ABI_align_preserved(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { + static const char *const Strings[] = { "Not Required", "8-byte data alignment", "8-byte data and code alignment", "Reserved" }; @@ -365,7 +371,7 @@ void ARMAttributeParser::ABI_align_preserved(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::ABI_enum_size(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { + static const char *const Strings[] = { "Not Permitted", "Packed", "Int32", "External Int32" }; @@ -377,7 +383,7 @@ void ARMAttributeParser::ABI_enum_size(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::ABI_HardFP_use(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { + static const char *const Strings[] = { "Tag_FP_arch", "Single-Precision", "Reserved", "Tag_FP_arch (deprecated)" }; @@ -389,7 +395,7 @@ void ARMAttributeParser::ABI_HardFP_use(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::ABI_VFP_args(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { + static const char *const Strings[] = { "AAPCS", "AAPCS VFP", "Custom", "Not Permitted" }; @@ -401,7 +407,7 @@ void ARMAttributeParser::ABI_VFP_args(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::ABI_WMMX_args(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { "AAPCS", "iWMMX", "Custom" }; + static const char *const Strings[] = { "AAPCS", "iWMMX", "Custom" }; uint64_t Value = ParseInteger(Data, Offset); StringRef ValueDesc = @@ -412,7 +418,7 @@ void ARMAttributeParser::ABI_WMMX_args(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::ABI_optimization_goals(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { + static const char *const Strings[] = { "None", "Speed", "Aggressive Speed", "Size", "Aggressive Size", "Debugging", "Best Debugging" }; @@ -426,7 +432,7 @@ void ARMAttributeParser::ABI_optimization_goals(AttrType Tag, void ARMAttributeParser::ABI_FP_optimization_goals(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { + static const char *const Strings[] = { "None", "Speed", "Aggressive Speed", "Size", "Aggressive Size", "Accuracy", "Best Accuracy" }; @@ -461,7 +467,7 @@ void ARMAttributeParser::compatibility(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::CPU_unaligned_access(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { "Not Permitted", "v6-style" }; + static const char *const Strings[] = { "Not Permitted", "v6-style" }; uint64_t Value = ParseInteger(Data, Offset); StringRef ValueDesc = @@ -471,7 +477,7 @@ void ARMAttributeParser::CPU_unaligned_access(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::FP_HP_extension(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { "If Available", "Permitted" }; + static const char *const Strings[] = { "If Available", "Permitted" }; uint64_t Value = ParseInteger(Data, Offset); StringRef ValueDesc = @@ -481,7 +487,7 @@ void ARMAttributeParser::FP_HP_extension(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::ABI_FP_16bit_format(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { "Not Permitted", "IEEE-754", "VFPv3" }; + static const char *const Strings[] = { "Not Permitted", "IEEE-754", "VFPv3" }; uint64_t Value = ParseInteger(Data, Offset); StringRef ValueDesc = @@ -491,7 +497,7 @@ void ARMAttributeParser::ABI_FP_16bit_format(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::MPextension_use(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { "Not Permitted", "Permitted" }; + static const char *const Strings[] = { "Not Permitted", "Permitted" }; uint64_t Value = ParseInteger(Data, Offset); StringRef ValueDesc = @@ -501,7 +507,7 @@ void ARMAttributeParser::MPextension_use(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::DIV_use(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { + static const char *const Strings[] = { "If Available", "Not Permitted", "Permitted" }; @@ -513,7 +519,7 @@ void ARMAttributeParser::DIV_use(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::T2EE_use(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { "Not Permitted", "Permitted" }; + static const char *const Strings[] = { "Not Permitted", "Permitted" }; uint64_t Value = ParseInteger(Data, Offset); StringRef ValueDesc = @@ -523,7 +529,7 @@ void ARMAttributeParser::T2EE_use(AttrType Tag, const uint8_t *Data, void ARMAttributeParser::Virtualization_use(AttrType Tag, const uint8_t *Data, uint32_t &Offset) { - static const char *Strings[] = { + static const char *const Strings[] = { "Not Permitted", "TrustZone", "Virtualization Extensions", "TrustZone + Virtualization Extensions" }; diff --git a/tools/llvm-readobj/ARMEHABIPrinter.h b/tools/llvm-readobj/ARMEHABIPrinter.h index dd2490d503db..beb5fd4ea042 100644 --- a/tools/llvm-readobj/ARMEHABIPrinter.h +++ b/tools/llvm-readobj/ARMEHABIPrinter.h @@ -305,13 +305,15 @@ void OpcodeDecoder::Decode(const uint8_t *Opcodes, off_t Offset, size_t Length) template <typename ET> class PrinterContext { - StreamWriter &SW; - const object::ELFFile<ET> *ELF; - typedef typename object::ELFFile<ET>::Elf_Sym Elf_Sym; typedef typename object::ELFFile<ET>::Elf_Shdr Elf_Shdr; + typedef typename object::ELFFile<ET>::Elf_Rel Elf_Rel; + typedef typename object::ELFFile<ET>::Elf_Word Elf_Word; - typedef typename object::ELFFile<ET>::Elf_Rel_Iter Elf_Rel_iterator; + StreamWriter &SW; + const object::ELFFile<ET> *ELF; + const Elf_Shdr *Symtab; + ArrayRef<Elf_Word> ShndxTable; static const size_t IndexTableEntrySize; @@ -332,8 +334,9 @@ class PrinterContext { void PrintOpcodes(const uint8_t *Entry, size_t Length, off_t Offset) const; public: - PrinterContext(StreamWriter &Writer, const object::ELFFile<ET> *File) - : SW(Writer), ELF(File) {} + PrinterContext(StreamWriter &SW, const object::ELFFile<ET> *ELF, + const Elf_Shdr *Symtab) + : SW(SW), ELF(ELF), Symtab(Symtab) {} void PrintUnwindInformation() const; }; @@ -345,10 +348,14 @@ template <typename ET> ErrorOr<StringRef> PrinterContext<ET>::FunctionAtAddress(unsigned Section, uint64_t Address) const { - for (const Elf_Sym &Sym : ELF->symbols()) + ErrorOr<StringRef> StrTableOrErr = ELF->getStringTableForSymtab(*Symtab); + error(StrTableOrErr.getError()); + StringRef StrTable = *StrTableOrErr; + + for (const Elf_Sym &Sym : ELF->symbols(Symtab)) if (Sym.st_shndx == Section && Sym.st_value == Address && Sym.getType() == ELF::STT_FUNC) - return ELF->getSymbolName(&Sym, false); + return Sym.getName(StrTable); return readobj_error::unknown_symbol; } @@ -365,24 +372,29 @@ PrinterContext<ET>::FindExceptionTable(unsigned IndexSectionIndex, /// table. for (const Elf_Shdr &Sec : ELF->sections()) { - if (Sec.sh_type == ELF::SHT_REL && Sec.sh_info == IndexSectionIndex) { - for (Elf_Rel_iterator RI = ELF->rel_begin(&Sec), RE = ELF->rel_end(&Sec); - RI != RE; ++RI) { - if (RI->r_offset == static_cast<unsigned>(IndexTableOffset)) { - typename object::ELFFile<ET>::Elf_Rela RelA; - RelA.r_offset = RI->r_offset; - RelA.r_info = RI->r_info; - RelA.r_addend = 0; + if (Sec.sh_type != ELF::SHT_REL || Sec.sh_info != IndexSectionIndex) + continue; - std::pair<const Elf_Shdr *, const Elf_Sym *> Symbol = - ELF->getRelocationSymbol(&Sec, &RelA); + ErrorOr<const Elf_Shdr *> SymTabOrErr = ELF->getSection(Sec.sh_link); + error(SymTabOrErr.getError()); + const Elf_Shdr *SymTab = *SymTabOrErr; - ErrorOr<const Elf_Shdr *> Ret = ELF->getSection(Symbol.second); - if (std::error_code EC = Ret.getError()) - report_fatal_error(EC.message()); - return *Ret; - } - } + for (const Elf_Rel &R : ELF->rels(&Sec)) { + if (R.r_offset != static_cast<unsigned>(IndexTableOffset)) + continue; + + typename object::ELFFile<ET>::Elf_Rela RelA; + RelA.r_offset = R.r_offset; + RelA.r_info = R.r_info; + RelA.r_addend = 0; + + const Elf_Sym *Symbol = ELF->getRelocationSymbol(&RelA, SymTab); + + ErrorOr<const Elf_Shdr *> Ret = + ELF->getSection(Symbol, SymTab, ShndxTable); + if (std::error_code EC = Ret.getError()) + report_fatal_error(EC.message()); + return *Ret; } } return nullptr; diff --git a/tools/llvm-readobj/ARMWinEHPrinter.cpp b/tools/llvm-readobj/ARMWinEHPrinter.cpp index bf5ff8e1d031..650955d1d75c 100644 --- a/tools/llvm-readobj/ARMWinEHPrinter.cpp +++ b/tools/llvm-readobj/ARMWinEHPrinter.cpp @@ -630,9 +630,10 @@ bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF, SW.printString("ExceptionRecord", formatSymbol(*Name, Address)); - section_iterator SI = COFF.section_end(); - if (XDataRecord->getSection(SI)) + ErrorOr<section_iterator> SIOrErr = XDataRecord->getSection(); + if (!SIOrErr) return false; + section_iterator SI = *SIOrErr; return dumpXDataRecord(COFF, *SI, FunctionAddress, Address); } else { diff --git a/tools/llvm-readobj/CMakeLists.txt b/tools/llvm-readobj/CMakeLists.txt index 87407a258021..a06b678780cb 100644 --- a/tools/llvm-readobj/CMakeLists.txt +++ b/tools/llvm-readobj/CMakeLists.txt @@ -7,6 +7,7 @@ add_llvm_tool(llvm-readobj ARMAttributeParser.cpp ARMWinEHPrinter.cpp COFFDumper.cpp + COFFImportDumper.cpp ELFDumper.cpp Error.cpp llvm-readobj.cpp diff --git a/tools/llvm-readobj/COFFDumper.cpp b/tools/llvm-readobj/COFFDumper.cpp index cf897d7cb484..516d1cf8057b 100644 --- a/tools/llvm-readobj/COFFDumper.cpp +++ b/tools/llvm-readobj/COFFDumper.cpp @@ -60,6 +60,7 @@ public: void printCOFFExports() override; void printCOFFDirectives() override; void printCOFFBaseReloc() override; + void printCodeViewDebugInfo() override; void printStackMap() const override; private: void printSymbol(const SymbolRef &Sym); @@ -71,7 +72,7 @@ private: void printBaseOfDataField(const pe32_header *Hdr); void printBaseOfDataField(const pe32plus_header *Hdr); - void printCodeViewDebugInfo(const SectionRef &Section); + void printCodeViewSection(const SectionRef &Section); void printCodeViewSymbolsSubsection(StringRef Subsection, const SectionRef &Section, @@ -219,6 +220,7 @@ static const EnumEntry<COFF::DLLCharacteristics> PEDLLCharacteristics[] = { static const EnumEntry<COFF::SectionCharacteristics> ImageSectionCharacteristics[] = { + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_TYPE_NOLOAD ), LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_TYPE_NO_PAD ), LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_CNT_CODE ), LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_SCN_CNT_INITIALIZED_DATA ), @@ -385,14 +387,12 @@ void COFFDumper::printFileHeaders() { // Print PE header. This header does not exist if this is an object file and // not an executable. const pe32_header *PEHeader = nullptr; - if (error(Obj->getPE32Header(PEHeader))) - return; + error(Obj->getPE32Header(PEHeader)); if (PEHeader) printPEHeader<pe32_header>(PEHeader); const pe32plus_header *PEPlusHeader = nullptr; - if (error(Obj->getPE32PlusHeader(PEPlusHeader))) - return; + error(Obj->getPE32PlusHeader(PEPlusHeader)); if (PEPlusHeader) printPEHeader<pe32plus_header>(PEPlusHeader); @@ -475,10 +475,18 @@ void COFFDumper::printBaseOfDataField(const pe32_header *Hdr) { void COFFDumper::printBaseOfDataField(const pe32plus_header *) {} -void COFFDumper::printCodeViewDebugInfo(const SectionRef &Section) { +void COFFDumper::printCodeViewDebugInfo() { + for (const SectionRef &S : Obj->sections()) { + StringRef SecName; + error(S.getName(SecName)); + if (SecName == ".debug$S") + printCodeViewSection(S); + } +} + +void COFFDumper::printCodeViewSection(const SectionRef &Section) { StringRef Data; - if (error(Section.getContents(Data))) - return; + error(Section.getContents(Data)); SmallVector<StringRef, 10> FunctionNames; StringMap<StringRef> FunctionLineTables; @@ -518,8 +526,7 @@ void COFFDumper::printCodeViewDebugInfo(const SectionRef &Section) { switch (SubSectionType) { case COFF::DEBUG_SYMBOL_SUBSECTION: - if (opts::SectionSymbols) - printCodeViewSymbolsSubsection(Contents, Section, Offset); + 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 @@ -533,19 +540,18 @@ void COFFDumper::printCodeViewDebugInfo(const SectionRef &Section) { return; } - StringRef FunctionName; - if (error(resolveSymbolName(Obj->getCOFFSection(Section), Offset, - FunctionName))) - return; - W.printString("FunctionName", FunctionName); - if (FunctionLineTables.count(FunctionName) != 0) { + StringRef LinkageName; + error(resolveSymbolName(Obj->getCOFFSection(Section), Offset, + LinkageName)); + W.printString("LinkageName", LinkageName); + if (FunctionLineTables.count(LinkageName) != 0) { // Saw debug info for this function already? error(object_error::parse_failed); return; } - FunctionLineTables[FunctionName] = Contents; - FunctionNames.push_back(FunctionName); + FunctionLineTables[LinkageName] = Contents; + FunctionNames.push_back(LinkageName); break; } case COFF::DEBUG_STRING_TABLE_SUBSECTION: @@ -582,7 +588,7 @@ void COFFDumper::printCodeViewDebugInfo(const SectionRef &Section) { for (unsigned I = 0, E = FunctionNames.size(); I != E; ++I) { StringRef Name = FunctionNames[I]; ListScope S(W, "FunctionLineTable"); - W.printString("FunctionName", Name); + W.printString("LinkageName", Name); DataExtractor DE(FunctionLineTables[Name], true, 4); uint32_t Offset = 6; // Skip relocations. @@ -695,9 +701,8 @@ void COFFDumper::printCodeViewSymbolsSubsection(StringRef Subsection, uint32_t CodeSize = DE.getU32(&Offset); DE.getU8(&Offset, Unused, 12); StringRef SectionName; - if (error(resolveSymbolName(Obj->getCOFFSection(Section), - OffsetInSection + Offset, SectionName))) - return; + error(resolveSymbolName(Obj->getCOFFSection(Section), + OffsetInSection + Offset, SectionName)); Offset += 4; DE.getU8(&Offset, Unused, 3); StringRef DisplayName = DE.getCStr(&Offset); @@ -748,8 +753,7 @@ void COFFDumper::printSections() { const coff_section *Section = Obj->getCOFFSection(Sec); StringRef Name; - if (error(Sec.getName(Name))) - Name = ""; + error(Sec.getName(Name)); DictScope D(W, "Section"); W.printNumber("Number", SectionNumber); @@ -782,14 +786,10 @@ void COFFDumper::printSections() { } } - if (Name == ".debug$S" && opts::CodeView) - printCodeViewDebugInfo(Sec); - if (opts::SectionData && !(Section->Characteristics & COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA)) { StringRef Data; - if (error(Sec.getContents(Data))) - break; + error(Sec.getContents(Data)); W.printBinaryBlock("SectionData", Data); } @@ -803,8 +803,7 @@ void COFFDumper::printRelocations() { for (const SectionRef &Section : Obj->sections()) { ++SectionNumber; StringRef Name; - if (error(Section.getName(Name))) - continue; + error(Section.getName(Name)); bool PrintedGroup = false; for (const RelocationRef &Reloc : Section.relocations()) { @@ -834,8 +833,7 @@ void COFFDumper::printRelocation(const SectionRef &Section, symbol_iterator Symbol = Reloc.getSymbol(); if (Symbol != Obj->symbol_end()) { ErrorOr<StringRef> SymbolNameOrErr = Symbol->getName(); - if (error(SymbolNameOrErr.getError())) - return; + error(SymbolNameOrErr.getError()); SymbolName = *SymbolNameOrErr; } @@ -914,8 +912,7 @@ void COFFDumper::printSymbol(const SymbolRef &Sym) { 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))) - break; + error(getSymbolAuxData(Obj, Symbol, I, Aux)); DictScope AS(W, "AuxFunctionDef"); W.printNumber("TagIndex", Aux->TagIndex); @@ -925,8 +922,7 @@ void COFFDumper::printSymbol(const SymbolRef &Sym) { } else if (Symbol.isAnyUndefined()) { const coff_aux_weak_external *Aux; - if (error(getSymbolAuxData(Obj, Symbol, I, Aux))) - break; + error(getSymbolAuxData(Obj, Symbol, I, Aux)); ErrorOr<COFFSymbolRef> Linked = Obj->getSymbol(Aux->TagIndex); StringRef LinkedName; @@ -943,8 +939,7 @@ void COFFDumper::printSymbol(const SymbolRef &Sym) { } else if (Symbol.isFileRecord()) { const char *FileName; - if (error(getSymbolAuxData(Obj, Symbol, I, FileName))) - break; + error(getSymbolAuxData(Obj, Symbol, I, FileName)); DictScope AS(W, "AuxFileRecord"); @@ -954,8 +949,7 @@ void COFFDumper::printSymbol(const SymbolRef &Sym) { break; } else if (Symbol.isSectionDefinition()) { const coff_aux_section_definition *Aux; - if (error(getSymbolAuxData(Obj, Symbol, I, Aux))) - break; + error(getSymbolAuxData(Obj, Symbol, I, Aux)); int32_t AuxNumber = Aux->getNumber(Symbol.isBigObj()); @@ -986,8 +980,7 @@ void COFFDumper::printSymbol(const SymbolRef &Sym) { } } else if (Symbol.isCLRToken()) { const coff_aux_clr_token *Aux; - if (error(getSymbolAuxData(Obj, Symbol, I, Aux))) - break; + error(getSymbolAuxData(Obj, Symbol, I, Aux)); ErrorOr<COFFSymbolRef> ReferredSym = Obj->getSymbol(Aux->SymbolTableIndex); @@ -1040,9 +1033,9 @@ void COFFDumper::printImportedSymbols( iterator_range<imported_symbol_iterator> Range) { for (const ImportedSymbolRef &I : Range) { StringRef Sym; - if (error(I.getSymbolName(Sym))) return; + error(I.getSymbolName(Sym)); uint16_t Ordinal; - if (error(I.getOrdinal(Ordinal))) return; + error(I.getOrdinal(Ordinal)); W.printNumber("Symbol", Sym, Ordinal); } } @@ -1054,12 +1047,12 @@ void COFFDumper::printDelayImportedSymbols( for (const ImportedSymbolRef &S : Range) { DictScope Import(W, "Import"); StringRef Sym; - if (error(S.getSymbolName(Sym))) return; + error(S.getSymbolName(Sym)); uint16_t Ordinal; - if (error(S.getOrdinal(Ordinal))) return; + error(S.getOrdinal(Ordinal)); W.printNumber("Symbol", Sym, Ordinal); uint64_t Addr; - if (error(I.getImportAddress(Index++, Addr))) return; + error(I.getImportAddress(Index++, Addr)); W.printHex("Address", Addr); } } @@ -1069,12 +1062,12 @@ void COFFDumper::printCOFFImports() { for (const ImportDirectoryEntryRef &I : Obj->import_directories()) { DictScope Import(W, "Import"); StringRef Name; - if (error(I.getName(Name))) return; + error(I.getName(Name)); W.printString("Name", Name); uint32_t Addr; - if (error(I.getImportLookupTableRVA(Addr))) return; + error(I.getImportLookupTableRVA(Addr)); W.printHex("ImportLookupTableRVA", Addr); - if (error(I.getImportAddressTableRVA(Addr))) return; + error(I.getImportAddressTableRVA(Addr)); W.printHex("ImportAddressTableRVA", Addr); printImportedSymbols(I.imported_symbols()); } @@ -1083,10 +1076,10 @@ void COFFDumper::printCOFFImports() { for (const DelayImportDirectoryEntryRef &I : Obj->delay_import_directories()) { DictScope Import(W, "DelayImport"); StringRef Name; - if (error(I.getName(Name))) return; + error(I.getName(Name)); W.printString("Name", Name); const delay_import_directory_table_entry *Table; - if (error(I.getDelayImportTable(Table))) return; + error(I.getDelayImportTable(Table)); W.printHex("Attributes", Table->Attributes); W.printHex("ModuleHandle", Table->ModuleHandle); W.printHex("ImportAddressTable", Table->DelayImportAddressTable); @@ -1104,12 +1097,9 @@ void COFFDumper::printCOFFExports() { StringRef Name; uint32_t Ordinal, RVA; - if (error(E.getSymbolName(Name))) - continue; - if (error(E.getOrdinal(Ordinal))) - continue; - if (error(E.getExportRVA(RVA))) - continue; + error(E.getSymbolName(Name)); + error(E.getOrdinal(Ordinal)); + error(E.getExportRVA(RVA)); W.printNumber("Ordinal", Ordinal); W.printString("Name", Name); @@ -1122,13 +1112,11 @@ void COFFDumper::printCOFFDirectives() { StringRef Contents; StringRef Name; - if (error(Section.getName(Name))) - continue; + error(Section.getName(Name)); if (Name != ".drectve") continue; - if (error(Section.getContents(Contents))) - return; + error(Section.getContents(Contents)); W.printString("Directive(s)", Contents); } @@ -1152,10 +1140,8 @@ void COFFDumper::printCOFFBaseReloc() { 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; + error(I.getRVA(RVA)); + error(I.getType(Type)); DictScope Import(W, "Entry"); W.printString("Type", getBaseRelocTypeName(Type)); W.printHex("Address", RVA); diff --git a/tools/llvm-readobj/COFFImportDumper.cpp b/tools/llvm-readobj/COFFImportDumper.cpp new file mode 100644 index 000000000000..83715e60f057 --- /dev/null +++ b/tools/llvm-readobj/COFFImportDumper.cpp @@ -0,0 +1,52 @@ +//===-- COFFImportDumper.cpp - COFF import library dumper -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file implements the COFF import library dumper for llvm-readobj. +/// +//===----------------------------------------------------------------------===// + +#include "Error.h" +#include "ObjDumper.h" +#include "llvm-readobj.h" +#include "llvm/Object/COFF.h" +#include "llvm/Object/COFFImportFile.h" +#include "llvm/Support/COFF.h" + +using namespace llvm::object; + +namespace llvm { + +void dumpCOFFImportFile(const COFFImportFile *File) { + outs() << '\n'; + outs() << "File: " << File->getFileName() << "\n"; + outs() << "Format: COFF-import-file\n"; + + const coff_import_header *H = File->getCOFFImportHeader(); + switch (H->getType()) { + case COFF::IMPORT_CODE: outs() << "Type: code\n"; break; + case COFF::IMPORT_DATA: outs() << "Type: data\n"; break; + case COFF::IMPORT_CONST: outs() << "Type: const\n"; break; + } + + switch (H->getNameType()) { + case COFF::IMPORT_ORDINAL: outs() << "Name type: ordinal\n"; break; + case COFF::IMPORT_NAME: outs() << "Name type: name\n"; break; + case COFF::IMPORT_NAME_NOPREFIX: outs() << "Name type: noprefix\n"; break; + case COFF::IMPORT_NAME_UNDECORATE: outs() << "Name type: undecorate\n"; break; + } + + for (const object::BasicSymbolRef &Sym : File->symbols()) { + outs() << "Symbol: "; + Sym.printName(outs()); + outs() << "\n"; + } +} + +} // namespace llvm diff --git a/tools/llvm-readobj/ELFDumper.cpp b/tools/llvm-readobj/ELFDumper.cpp index 1cdf5529c080..02397f382848 100644 --- a/tools/llvm-readobj/ELFDumper.cpp +++ b/tools/llvm-readobj/ELFDumper.cpp @@ -42,8 +42,7 @@ namespace { template<typename ELFT> class ELFDumper : public ObjDumper { public: - ELFDumper(const ELFFile<ELFT> *Obj, StreamWriter &Writer) - : ObjDumper(Writer), Obj(Obj) {} + ELFDumper(const ELFFile<ELFT> *Obj, StreamWriter &Writer); void printFileHeaders() override; void printSections() override; @@ -57,6 +56,9 @@ public: void printNeededLibraries() override; void printProgramHeaders() override; void printHashTable() override; + void printGnuHashTable() override; + void printLoadName() override; + void printVersionInfo() override; void printAttributes() override; void printMipsPLTGOT() override; @@ -69,13 +71,114 @@ private: typedef ELFFile<ELFT> ELFO; typedef typename ELFO::Elf_Shdr Elf_Shdr; typedef typename ELFO::Elf_Sym Elf_Sym; + typedef typename ELFO::Elf_Dyn Elf_Dyn; + typedef typename ELFO::Elf_Dyn_Range Elf_Dyn_Range; + typedef typename ELFO::Elf_Rel Elf_Rel; + typedef typename ELFO::Elf_Rela Elf_Rela; + typedef typename ELFO::Elf_Rela_Range Elf_Rela_Range; + typedef typename ELFO::Elf_Phdr Elf_Phdr; + typedef typename ELFO::Elf_Half Elf_Half; + typedef typename ELFO::Elf_Hash Elf_Hash; + typedef typename ELFO::Elf_GnuHash Elf_GnuHash; + typedef typename ELFO::Elf_Ehdr Elf_Ehdr; + typedef typename ELFO::Elf_Word Elf_Word; + typedef typename ELFO::uintX_t uintX_t; + typedef typename ELFO::Elf_Versym Elf_Versym; + typedef typename ELFO::Elf_Verneed Elf_Verneed; + typedef typename ELFO::Elf_Vernaux Elf_Vernaux; + typedef typename ELFO::Elf_Verdef Elf_Verdef; + typedef typename ELFO::Elf_Verdaux Elf_Verdaux; - void printSymbol(const Elf_Sym *Symbol, bool IsDynamic); + /// \brief Represents a region described by entries in the .dynamic table. + struct DynRegionInfo { + DynRegionInfo() : Addr(nullptr), Size(0), EntSize(0) {} + /// \brief Address in current address space. + const void *Addr; + /// \brief Size in bytes of the region. + uintX_t Size; + /// \brief Size of each entity in the region. + uintX_t EntSize; + }; + + void printSymbolsHelper(bool IsDynamic); + void printSymbol(const Elf_Sym *Symbol, const Elf_Shdr *SymTab, + StringRef StrTable, bool IsDynamic); void printRelocations(const Elf_Shdr *Sec); - void printRelocation(const Elf_Shdr *Sec, typename ELFO::Elf_Rela Rel); + void printRelocation(Elf_Rela Rel, const Elf_Shdr *SymTab); + void printValue(uint64_t Type, uint64_t Value); + + const Elf_Rela *dyn_rela_begin() const; + const Elf_Rela *dyn_rela_end() const; + Elf_Rela_Range dyn_relas() const; + StringRef getDynamicString(uint64_t Offset) const; + const Elf_Dyn *dynamic_table_begin() const { + ErrorOr<const Elf_Dyn *> Ret = Obj->dynamic_table_begin(DynamicProgHeader); + error(Ret.getError()); + return *Ret; + } + const Elf_Dyn *dynamic_table_end() const { + ErrorOr<const Elf_Dyn *> Ret = Obj->dynamic_table_end(DynamicProgHeader); + error(Ret.getError()); + return *Ret; + } + StringRef getSymbolVersion(StringRef StrTab, const Elf_Sym *symb, + bool &IsDefault); + void LoadVersionMap(); + void LoadVersionNeeds(const Elf_Shdr *ec) const; + void LoadVersionDefs(const Elf_Shdr *sec) const; const ELFO *Obj; + DynRegionInfo DynRelaRegion; + const Elf_Phdr *DynamicProgHeader = nullptr; + StringRef DynamicStringTable; + const Elf_Sym *DynSymStart = nullptr; + StringRef SOName; + const Elf_Hash *HashTable = nullptr; + const Elf_GnuHash *GnuHashTable = nullptr; + const Elf_Shdr *DotDynSymSec = nullptr; + const Elf_Shdr *DotSymtabSec = nullptr; + ArrayRef<Elf_Word> ShndxTable; + + const Elf_Shdr *dot_gnu_version_sec = nullptr; // .gnu.version + const Elf_Shdr *dot_gnu_version_r_sec = nullptr; // .gnu.version_r + const Elf_Shdr *dot_gnu_version_d_sec = nullptr; // .gnu.version_d + + // Records for each version index the corresponding Verdef or Vernaux entry. + // This is filled the first time LoadVersionMap() is called. + class VersionMapEntry : public PointerIntPair<const void *, 1> { + public: + // If the integer is 0, this is an Elf_Verdef*. + // If the integer is 1, this is an Elf_Vernaux*. + VersionMapEntry() : PointerIntPair<const void *, 1>(nullptr, 0) {} + VersionMapEntry(const Elf_Verdef *verdef) + : PointerIntPair<const void *, 1>(verdef, 0) {} + VersionMapEntry(const Elf_Vernaux *vernaux) + : PointerIntPair<const void *, 1>(vernaux, 1) {} + bool isNull() const { return getPointer() == nullptr; } + bool isVerdef() const { return !isNull() && getInt() == 0; } + bool isVernaux() const { return !isNull() && getInt() == 1; } + const Elf_Verdef *getVerdef() const { + return isVerdef() ? (const Elf_Verdef *)getPointer() : nullptr; + } + const Elf_Vernaux *getVernaux() const { + return isVernaux() ? (const Elf_Vernaux *)getPointer() : nullptr; + } + }; + mutable SmallVector<VersionMapEntry, 16> VersionMap; + +public: + Elf_Dyn_Range dynamic_table() const { + ErrorOr<Elf_Dyn_Range> Ret = Obj->dynamic_table(DynamicProgHeader); + error(Ret.getError()); + return *Ret; + } + + std::string getFullSymbolName(const Elf_Sym *Symbol, StringRef StrTable, + bool IsDynamic); + const Elf_Shdr *getDotDynSymSec() const { return DotDynSymSec; } + const Elf_Shdr *getDotSymtabSec() const { return DotSymtabSec; } + ArrayRef<Elf_Word> getShndxTable() { return ShndxTable; } }; template <class T> T errorOrDefault(ErrorOr<T> Val, T Default = T()) { @@ -122,30 +225,246 @@ std::error_code createELFDumper(const object::ObjectFile *Obj, } // namespace llvm -template <typename ELFO> -static std::string getFullSymbolName(const ELFO &Obj, - const typename ELFO::Elf_Sym *Symbol, - bool IsDynamic) { - StringRef SymbolName = errorOrDefault(Obj.getSymbolName(Symbol, IsDynamic)); +// Iterate through the versions needed section, and place each Elf_Vernaux +// in the VersionMap according to its index. +template <class ELFT> +void ELFDumper<ELFT>::LoadVersionNeeds(const Elf_Shdr *sec) const { + unsigned vn_size = sec->sh_size; // Size of section in bytes + unsigned vn_count = sec->sh_info; // Number of Verneed entries + const char *sec_start = (const char *)Obj->base() + sec->sh_offset; + const char *sec_end = sec_start + vn_size; + // The first Verneed entry is at the start of the section. + const char *p = sec_start; + for (unsigned i = 0; i < vn_count; i++) { + if (p + sizeof(Elf_Verneed) > sec_end) + report_fatal_error("Section ended unexpectedly while scanning " + "version needed records."); + const Elf_Verneed *vn = reinterpret_cast<const Elf_Verneed *>(p); + if (vn->vn_version != ELF::VER_NEED_CURRENT) + report_fatal_error("Unexpected verneed version"); + // Iterate through the Vernaux entries + const char *paux = p + vn->vn_aux; + for (unsigned j = 0; j < vn->vn_cnt; j++) { + if (paux + sizeof(Elf_Vernaux) > sec_end) + report_fatal_error("Section ended unexpected while scanning auxiliary " + "version needed records."); + const Elf_Vernaux *vna = reinterpret_cast<const Elf_Vernaux *>(paux); + size_t index = vna->vna_other & ELF::VERSYM_VERSION; + if (index >= VersionMap.size()) + VersionMap.resize(index + 1); + VersionMap[index] = VersionMapEntry(vna); + paux += vna->vna_next; + } + p += vn->vn_next; + } +} + +// Iterate through the version definitions, and place each Elf_Verdef +// in the VersionMap according to its index. +template <class ELFT> +void ELFDumper<ELFT>::LoadVersionDefs(const Elf_Shdr *sec) const { + unsigned vd_size = sec->sh_size; // Size of section in bytes + unsigned vd_count = sec->sh_info; // Number of Verdef entries + const char *sec_start = (const char *)Obj->base() + sec->sh_offset; + const char *sec_end = sec_start + vd_size; + // The first Verdef entry is at the start of the section. + const char *p = sec_start; + for (unsigned i = 0; i < vd_count; i++) { + if (p + sizeof(Elf_Verdef) > sec_end) + report_fatal_error("Section ended unexpectedly while scanning " + "version definitions."); + const Elf_Verdef *vd = reinterpret_cast<const Elf_Verdef *>(p); + if (vd->vd_version != ELF::VER_DEF_CURRENT) + report_fatal_error("Unexpected verdef version"); + size_t index = vd->vd_ndx & ELF::VERSYM_VERSION; + if (index >= VersionMap.size()) + VersionMap.resize(index + 1); + VersionMap[index] = VersionMapEntry(vd); + p += vd->vd_next; + } +} + +template <class ELFT> void ELFDumper<ELFT>::LoadVersionMap() { + // If there is no dynamic symtab or version table, there is nothing to do. + if (!DynSymStart || !dot_gnu_version_sec) + return; + + // Has the VersionMap already been loaded? + if (VersionMap.size() > 0) + return; + + // The first two version indexes are reserved. + // Index 0 is LOCAL, index 1 is GLOBAL. + VersionMap.push_back(VersionMapEntry()); + VersionMap.push_back(VersionMapEntry()); + + if (dot_gnu_version_d_sec) + LoadVersionDefs(dot_gnu_version_d_sec); + + if (dot_gnu_version_r_sec) + LoadVersionNeeds(dot_gnu_version_r_sec); +} + + +template <typename ELFO, class ELFT> +static void printVersionSymbolSection(ELFDumper<ELFT> *Dumper, + const ELFO *Obj, + const typename ELFO::Elf_Shdr *Sec, + StreamWriter &W) { + DictScope SS(W, "Version symbols"); + if (!Sec) + return; + StringRef Name = errorOrDefault(Obj->getSectionName(Sec)); + W.printNumber("Section Name", Name, Sec->sh_name); + W.printHex("Address", Sec->sh_addr); + W.printHex("Offset", Sec->sh_offset); + W.printNumber("Link", Sec->sh_link); + + const typename ELFO::Elf_Shdr *DynSymSec = Dumper->getDotDynSymSec(); + const uint8_t *P = (const uint8_t *)Obj->base() + Sec->sh_offset; + ErrorOr<StringRef> StrTableOrErr = + Obj->getStringTableForSymtab(*DynSymSec); + error(StrTableOrErr.getError()); + + // Same number of entries in the dynamic symbol table (DT_SYMTAB). + ListScope Syms(W, "Symbols"); + for (const typename ELFO::Elf_Sym &Sym : Obj->symbols(DynSymSec)) { + DictScope S(W, "Symbol"); + std::string FullSymbolName = + Dumper->getFullSymbolName(&Sym, *StrTableOrErr, true /* IsDynamic */); + W.printNumber("Version", *P); + W.printString("Name", FullSymbolName); + P += sizeof(typename ELFO::Elf_Half); + } +} + +template <typename ELFO, class ELFT> +static void printVersionDefinitionSection(ELFDumper<ELFT> *Dumper, + const ELFO *Obj, + const typename ELFO::Elf_Shdr *Sec, + StreamWriter &W) { + DictScope SD(W, "Version definition"); + if (!Sec) + return; + StringRef Name = errorOrDefault(Obj->getSectionName(Sec)); + W.printNumber("Section Name", Name, Sec->sh_name); + W.printHex("Address", Sec->sh_addr); + W.printHex("Offset", Sec->sh_offset); + W.printNumber("Link", Sec->sh_link); + + unsigned verdef_entries = 0; + // The number of entries in the section SHT_GNU_verdef + // is determined by DT_VERDEFNUM tag. + for (const typename ELFO::Elf_Dyn &Dyn : Dumper->dynamic_table()) { + if (Dyn.d_tag == DT_VERDEFNUM) + verdef_entries = Dyn.d_un.d_val; + } + const uint8_t *SecStartAddress = + (const uint8_t *)Obj->base() + Sec->sh_offset; + const uint8_t *SecEndAddress = SecStartAddress + Sec->sh_size; + const uint8_t *P = SecStartAddress; + ErrorOr<const typename ELFO::Elf_Shdr *> StrTabOrErr = + Obj->getSection(Sec->sh_link); + error(StrTabOrErr.getError()); + + ListScope Entries(W, "Entries"); + for (unsigned i = 0; i < verdef_entries; ++i) { + if (P + sizeof(typename ELFO::Elf_Verdef) > SecEndAddress) + report_fatal_error("invalid offset in the section"); + auto *VD = reinterpret_cast<const typename ELFO::Elf_Verdef *>(P); + DictScope Entry(W, "Entry"); + W.printHex("Offset", (uintptr_t)P - (uintptr_t)SecStartAddress); + W.printNumber("Rev", VD->vd_version); + // FIXME: print something more readable. + W.printNumber("Flags", VD->vd_flags); + W.printNumber("Index", VD->vd_ndx); + W.printNumber("Cnt", VD->vd_cnt); + W.printString("Name", StringRef((const char *)(Obj->base() + + (*StrTabOrErr)->sh_offset + + VD->getAux()->vda_name))); + P += VD->vd_next; + } +} + +template <typename ELFT> void ELFDumper<ELFT>::printVersionInfo() { + // Dump version symbol section. + printVersionSymbolSection(this, Obj, dot_gnu_version_sec, W); + + // Dump version definition section. + printVersionDefinitionSection(this, Obj, dot_gnu_version_d_sec, W); +} + +template <typename ELFT> +StringRef ELFDumper<ELFT>::getSymbolVersion(StringRef StrTab, + const Elf_Sym *symb, + bool &IsDefault) { + // This is a dynamic symbol. Look in the GNU symbol version table. + if (!dot_gnu_version_sec) { + // No version table. + IsDefault = false; + return StringRef(""); + } + + // Determine the position in the symbol table of this entry. + size_t entry_index = (reinterpret_cast<uintptr_t>(symb) - + reinterpret_cast<uintptr_t>(DynSymStart)) / + sizeof(Elf_Sym); + + // Get the corresponding version index entry + const Elf_Versym *vs = + Obj->template getEntry<Elf_Versym>(dot_gnu_version_sec, entry_index); + size_t version_index = vs->vs_index & ELF::VERSYM_VERSION; + + // Special markers for unversioned symbols. + if (version_index == ELF::VER_NDX_LOCAL || + version_index == ELF::VER_NDX_GLOBAL) { + IsDefault = false; + return StringRef(""); + } + + // Lookup this symbol in the version table + LoadVersionMap(); + if (version_index >= VersionMap.size() || VersionMap[version_index].isNull()) + reportError("Invalid version entry"); + const VersionMapEntry &entry = VersionMap[version_index]; + + // Get the version name string + size_t name_offset; + if (entry.isVerdef()) { + // The first Verdaux entry holds the name. + name_offset = entry.getVerdef()->getAux()->vda_name; + IsDefault = !(vs->vs_index & ELF::VERSYM_HIDDEN); + } else { + name_offset = entry.getVernaux()->vna_name; + IsDefault = false; + } + if (name_offset >= StrTab.size()) + reportError("Invalid string offset"); + return StringRef(StrTab.data() + name_offset); +} + +template <typename ELFT> +std::string ELFDumper<ELFT>::getFullSymbolName(const Elf_Sym *Symbol, + StringRef StrTable, + bool IsDynamic) { + StringRef SymbolName = errorOrDefault(Symbol->getName(StrTable)); if (!IsDynamic) return SymbolName; std::string FullSymbolName(SymbolName); bool IsDefault; - ErrorOr<StringRef> Version = - Obj.getSymbolVersion(nullptr, &*Symbol, IsDefault); - if (Version) { - FullSymbolName += (IsDefault ? "@@" : "@"); - FullSymbolName += *Version; - } else - error(Version.getError()); + StringRef Version = getSymbolVersion(StrTable, &*Symbol, IsDefault); + FullSymbolName += (IsDefault ? "@@" : "@"); + FullSymbolName += Version; return FullSymbolName; } template <typename ELFO> static void getSectionNameIndex(const ELFO &Obj, const typename ELFO::Elf_Sym *Symbol, + const typename ELFO::Elf_Shdr *SymTab, + ArrayRef<typename ELFO::Elf_Word> ShndxTable, StringRef &SectionName, unsigned &SectionIndex) { SectionIndex = Symbol->st_shndx; if (Symbol->isUndefined()) @@ -162,25 +481,26 @@ getSectionNameIndex(const ELFO &Obj, const typename ELFO::Elf_Sym *Symbol, SectionName = "Reserved"; else { if (SectionIndex == SHN_XINDEX) - SectionIndex = Obj.getExtendedSymbolTableIndex(&*Symbol); + SectionIndex = + Obj.getExtendedSymbolTableIndex(Symbol, SymTab, ShndxTable); ErrorOr<const typename ELFO::Elf_Shdr *> Sec = Obj.getSection(SectionIndex); - if (!error(Sec.getError())) - SectionName = errorOrDefault(Obj.getSectionName(*Sec)); + error(Sec.getError()); + SectionName = errorOrDefault(Obj.getSectionName(*Sec)); } } -template <class ELFT> -static const typename ELFFile<ELFT>::Elf_Shdr * -findSectionByAddress(const ELFFile<ELFT> *Obj, uint64_t Addr) { +template <class ELFO> +static const typename ELFO::Elf_Shdr *findSectionByAddress(const ELFO *Obj, + uint64_t Addr) { for (const auto &Shdr : Obj->sections()) if (Shdr.sh_addr == Addr) return &Shdr; return nullptr; } -template <class ELFT> -static const typename ELFFile<ELFT>::Elf_Shdr * -findSectionByName(const ELFFile<ELFT> &Obj, StringRef Name) { +template <class ELFO> +static const typename ELFO::Elf_Shdr *findSectionByName(const ELFO &Obj, + StringRef Name) { for (const auto &Shdr : Obj.sections()) { if (Name == errorOrDefault(Obj.getSectionName(&Shdr))) return &Shdr; @@ -409,6 +729,12 @@ static const EnumEntry<unsigned> ElfSymbolTypes[] = { { "GNU_IFunc", ELF::STT_GNU_IFUNC } }; +static const EnumEntry<unsigned> AMDGPUSymbolTypes[] = { + { "AMDGPU_HSA_KERNEL", ELF::STT_AMDGPU_HSA_KERNEL }, + { "AMDGPU_HSA_INDIRECT_FUNCTION", ELF::STT_AMDGPU_HSA_INDIRECT_FUNCTION }, + { "AMDGPU_HSA_METADATA", ELF::STT_AMDGPU_HSA_METADATA } +}; + static const char *getElfSectionType(unsigned Arch, unsigned Type) { switch (Arch) { case ELF::EM_ARM: @@ -473,13 +799,24 @@ static const EnumEntry<unsigned> ElfSectionFlags[] = { LLVM_READOBJ_ENUM_ENT(ELF, SHF_TLS ), LLVM_READOBJ_ENUM_ENT(ELF, XCORE_SHF_CP_SECTION), LLVM_READOBJ_ENUM_ENT(ELF, XCORE_SHF_DP_SECTION), - LLVM_READOBJ_ENUM_ENT(ELF, SHF_MIPS_NOSTRIP ) + LLVM_READOBJ_ENUM_ENT(ELF, SHF_MIPS_NOSTRIP ), + LLVM_READOBJ_ENUM_ENT(ELF, SHF_AMDGPU_HSA_GLOBAL), + LLVM_READOBJ_ENUM_ENT(ELF, SHF_AMDGPU_HSA_READONLY), + LLVM_READOBJ_ENUM_ENT(ELF, SHF_AMDGPU_HSA_CODE), + LLVM_READOBJ_ENUM_ENT(ELF, SHF_AMDGPU_HSA_AGENT) }; static const char *getElfSegmentType(unsigned Arch, unsigned Type) { // Check potentially overlapped processor-specific // program header type. switch (Arch) { + case ELF::EM_AMDGPU: + switch (Type) { + LLVM_READOBJ_ENUM_CASE(ELF, PT_AMDGPU_HSA_LOAD_GLOBAL_PROGRAM); + LLVM_READOBJ_ENUM_CASE(ELF, PT_AMDGPU_HSA_LOAD_GLOBAL_AGENT); + LLVM_READOBJ_ENUM_CASE(ELF, PT_AMDGPU_HSA_LOAD_READONLY_AGENT); + LLVM_READOBJ_ENUM_CASE(ELF, PT_AMDGPU_HSA_LOAD_CODE_AGENT); + } case ELF::EM_ARM: switch (Type) { LLVM_READOBJ_ENUM_CASE(ELF, PT_ARM_EXIDX); @@ -565,9 +902,138 @@ static const EnumEntry<unsigned> ElfHeaderMipsFlags[] = { LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_64R6) }; +template <typename ELFT> +ELFDumper<ELFT>::ELFDumper(const ELFFile<ELFT> *Obj, StreamWriter &Writer) + : ObjDumper(Writer), Obj(Obj) { + + SmallVector<const Elf_Phdr *, 4> LoadSegments; + for (const Elf_Phdr &Phdr : Obj->program_headers()) { + if (Phdr.p_type == ELF::PT_DYNAMIC) { + DynamicProgHeader = &Phdr; + continue; + } + if (Phdr.p_type != ELF::PT_LOAD || Phdr.p_filesz == 0) + continue; + LoadSegments.push_back(&Phdr); + } + + auto toMappedAddr = [&](uint64_t VAddr) -> const uint8_t * { + const Elf_Phdr **I = std::upper_bound( + LoadSegments.begin(), LoadSegments.end(), VAddr, compareAddr<ELFT>); + if (I == LoadSegments.begin()) + report_fatal_error("Virtual address is not in any segment"); + --I; + const Elf_Phdr &Phdr = **I; + uint64_t Delta = VAddr - Phdr.p_vaddr; + if (Delta >= Phdr.p_filesz) + report_fatal_error("Virtual address is not in any segment"); + return Obj->base() + Phdr.p_offset + Delta; + }; + + uint64_t SONameOffset = 0; + const char *StringTableBegin = nullptr; + uint64_t StringTableSize = 0; + for (const Elf_Dyn &Dyn : dynamic_table()) { + switch (Dyn.d_tag) { + case ELF::DT_HASH: + HashTable = + reinterpret_cast<const Elf_Hash *>(toMappedAddr(Dyn.getPtr())); + break; + case ELF::DT_GNU_HASH: + GnuHashTable = + reinterpret_cast<const Elf_GnuHash *>(toMappedAddr(Dyn.getPtr())); + break; + case ELF::DT_RELA: + DynRelaRegion.Addr = toMappedAddr(Dyn.getPtr()); + break; + case ELF::DT_RELASZ: + DynRelaRegion.Size = Dyn.getVal(); + break; + case ELF::DT_RELAENT: + DynRelaRegion.EntSize = Dyn.getVal(); + break; + case ELF::DT_SONAME: + SONameOffset = Dyn.getVal(); + break; + case ELF::DT_STRTAB: + StringTableBegin = (const char *)toMappedAddr(Dyn.getPtr()); + break; + case ELF::DT_STRSZ: + StringTableSize = Dyn.getVal(); + break; + case ELF::DT_SYMTAB: + DynSymStart = + reinterpret_cast<const Elf_Sym *>(toMappedAddr(Dyn.getPtr())); + break; + } + } + if (StringTableBegin) + DynamicStringTable = StringRef(StringTableBegin, StringTableSize); + if (SONameOffset) + SOName = getDynamicString(SONameOffset); + + for (const Elf_Shdr &Sec : Obj->sections()) { + switch (Sec.sh_type) { + case ELF::SHT_GNU_versym: + if (dot_gnu_version_sec != nullptr) + reportError("Multiple SHT_GNU_versym"); + dot_gnu_version_sec = &Sec; + break; + case ELF::SHT_GNU_verdef: + if (dot_gnu_version_d_sec != nullptr) + reportError("Multiple SHT_GNU_verdef"); + dot_gnu_version_d_sec = &Sec; + break; + case ELF::SHT_GNU_verneed: + if (dot_gnu_version_r_sec != nullptr) + reportError("Multilpe SHT_GNU_verneed"); + dot_gnu_version_r_sec = &Sec; + break; + case ELF::SHT_DYNSYM: + if (DotDynSymSec != nullptr) + reportError("Multilpe SHT_DYNSYM"); + DotDynSymSec = &Sec; + break; + case ELF::SHT_SYMTAB: + if (DotSymtabSec != nullptr) + reportError("Multilpe SHT_SYMTAB"); + DotSymtabSec = &Sec; + break; + case ELF::SHT_SYMTAB_SHNDX: { + ErrorOr<ArrayRef<Elf_Word>> TableOrErr = Obj->getSHNDXTable(Sec); + error(TableOrErr.getError()); + ShndxTable = *TableOrErr; + break; + } + } + } +} + +template <typename ELFT> +const typename ELFDumper<ELFT>::Elf_Rela * +ELFDumper<ELFT>::dyn_rela_begin() const { + if (DynRelaRegion.Size && DynRelaRegion.EntSize != sizeof(Elf_Rela)) + report_fatal_error("Invalid relocation entry size"); + return reinterpret_cast<const Elf_Rela *>(DynRelaRegion.Addr); +} + +template <typename ELFT> +const typename ELFDumper<ELFT>::Elf_Rela * +ELFDumper<ELFT>::dyn_rela_end() const { + uint64_t Size = DynRelaRegion.Size; + if (Size % sizeof(Elf_Rela)) + report_fatal_error("Invalid relocation table size"); + return dyn_rela_begin() + Size / sizeof(Elf_Rela); +} + +template <typename ELFT> +typename ELFDumper<ELFT>::Elf_Rela_Range ELFDumper<ELFT>::dyn_relas() const { + return make_range(dyn_rela_begin(), dyn_rela_end()); +} + template<class ELFT> void ELFDumper<ELFT>::printFileHeaders() { - const typename ELFO::Elf_Ehdr *Header = Obj->getHeader(); + const Elf_Ehdr *Header = Obj->getHeader(); { DictScope D(W, "ElfHeader"); @@ -618,7 +1084,7 @@ void ELFDumper<ELFT>::printSections() { ListScope SectionsD(W, "Sections"); int SectionIndex = -1; - for (const typename ELFO::Elf_Shdr &Sec : Obj->sections()) { + for (const Elf_Shdr &Sec : Obj->sections()) { ++SectionIndex; StringRef Name = errorOrDefault(Obj->getSectionName(&Sec)); @@ -645,12 +1111,18 @@ void ELFDumper<ELFT>::printSections() { if (opts::SectionSymbols) { ListScope D(W, "Symbols"); - for (const typename ELFO::Elf_Sym &Sym : Obj->symbols()) { - ErrorOr<const Elf_Shdr *> SymSec = Obj->getSection(&Sym); + const Elf_Shdr *Symtab = DotSymtabSec; + ErrorOr<StringRef> StrTableOrErr = Obj->getStringTableForSymtab(*Symtab); + error(StrTableOrErr.getError()); + StringRef StrTable = *StrTableOrErr; + + for (const Elf_Sym &Sym : Obj->symbols(Symtab)) { + ErrorOr<const Elf_Shdr *> SymSec = + Obj->getSection(&Sym, Symtab, ShndxTable); if (!SymSec) continue; if (*SymSec == &Sec) - printSymbol(&Sym, false); + printSymbol(&Sym, Symtab, StrTable, false); } } @@ -667,7 +1139,7 @@ void ELFDumper<ELFT>::printRelocations() { ListScope D(W, "Relocations"); int SectionNumber = -1; - for (const typename ELFO::Elf_Shdr &Sec : Obj->sections()) { + for (const Elf_Shdr &Sec : Obj->sections()) { ++SectionNumber; if (Sec.sh_type != ELF::SHT_REL && Sec.sh_type != ELF::SHT_RELA) @@ -689,29 +1161,25 @@ template<class ELFT> void ELFDumper<ELFT>::printDynamicRelocations() { W.startLine() << "Dynamic Relocations {\n"; W.indent(); - for (typename ELFO::Elf_Rela_Iter RelI = Obj->dyn_rela_begin(), - RelE = Obj->dyn_rela_end(); - RelI != RelE; ++RelI) { + for (const Elf_Rela &Rel : dyn_relas()) { SmallString<32> RelocName; - Obj->getRelocationTypeName(RelI->getType(Obj->isMips64EL()), RelocName); + Obj->getRelocationTypeName(Rel.getType(Obj->isMips64EL()), RelocName); StringRef SymbolName; - uint32_t SymIndex = RelI->getSymbol(Obj->isMips64EL()); - const typename ELFO::Elf_Sym *Sym = Obj->dynamic_symbol_begin() + SymIndex; - SymbolName = errorOrDefault(Obj->getSymbolName(Sym, true)); + uint32_t SymIndex = Rel.getSymbol(Obj->isMips64EL()); + const Elf_Sym *Sym = DynSymStart + SymIndex; + SymbolName = errorOrDefault(Sym->getName(DynamicStringTable)); if (opts::ExpandRelocs) { DictScope Group(W, "Relocation"); - W.printHex("Offset", RelI->r_offset); - W.printNumber("Type", RelocName, (int)RelI->getType(Obj->isMips64EL())); + W.printHex("Offset", Rel.r_offset); + W.printNumber("Type", RelocName, (int)Rel.getType(Obj->isMips64EL())); W.printString("Symbol", SymbolName.size() > 0 ? SymbolName : "-"); - W.printHex("Addend", RelI->r_addend); + W.printHex("Addend", Rel.r_addend); } else { raw_ostream& OS = W.startLine(); - OS << W.hex(RelI->r_offset) - << " " << RelocName - << " " << (SymbolName.size() > 0 ? SymbolName : "-") - << " " << W.hex(RelI->r_addend) - << "\n"; + OS << W.hex(Rel.r_offset) << " " << RelocName << " " + << (SymbolName.size() > 0 ? SymbolName : "-") << " " + << W.hex(Rel.r_addend) << "\n"; } } W.unindent(); @@ -720,51 +1188,43 @@ void ELFDumper<ELFT>::printDynamicRelocations() { template <class ELFT> void ELFDumper<ELFT>::printRelocations(const Elf_Shdr *Sec) { + ErrorOr<const Elf_Shdr *> SymTabOrErr = Obj->getSection(Sec->sh_link); + error(SymTabOrErr.getError()); + const Elf_Shdr *SymTab = *SymTabOrErr; + switch (Sec->sh_type) { case ELF::SHT_REL: - for (typename ELFO::Elf_Rel_Iter RI = Obj->rel_begin(Sec), - RE = Obj->rel_end(Sec); - RI != RE; ++RI) { - typename ELFO::Elf_Rela Rela; - Rela.r_offset = RI->r_offset; - Rela.r_info = RI->r_info; + for (const Elf_Rel &R : Obj->rels(Sec)) { + Elf_Rela Rela; + Rela.r_offset = R.r_offset; + Rela.r_info = R.r_info; Rela.r_addend = 0; - printRelocation(Sec, Rela); + printRelocation(Rela, SymTab); } break; case ELF::SHT_RELA: - for (typename ELFO::Elf_Rela_Iter RI = Obj->rela_begin(Sec), - RE = Obj->rela_end(Sec); - RI != RE; ++RI) { - printRelocation(Sec, *RI); - } + for (const Elf_Rela &R : Obj->relas(Sec)) + printRelocation(R, SymTab); break; } } template <class ELFT> -void ELFDumper<ELFT>::printRelocation(const Elf_Shdr *Sec, - typename ELFO::Elf_Rela Rel) { +void ELFDumper<ELFT>::printRelocation(Elf_Rela Rel, const Elf_Shdr *SymTab) { SmallString<32> RelocName; Obj->getRelocationTypeName(Rel.getType(Obj->isMips64EL()), RelocName); StringRef TargetName; - std::pair<const Elf_Shdr *, const Elf_Sym *> Sym = - Obj->getRelocationSymbol(Sec, &Rel); - if (Sym.second && Sym.second->getType() == ELF::STT_SECTION) { - ErrorOr<const Elf_Shdr *> Sec = Obj->getSection(Sym.second); - if (!error(Sec.getError())) { - ErrorOr<StringRef> SecName = Obj->getSectionName(*Sec); - if (SecName) - TargetName = SecName.get(); - } - } else if (Sym.first) { - const Elf_Shdr *SymTable = Sym.first; - ErrorOr<const Elf_Shdr *> StrTableSec = Obj->getSection(SymTable->sh_link); - if (!error(StrTableSec.getError())) { - ErrorOr<StringRef> StrTableOrErr = Obj->getStringTable(*StrTableSec); - if (!error(StrTableOrErr.getError())) - TargetName = errorOrDefault(Sym.second->getName(*StrTableOrErr)); - } + const Elf_Sym *Sym = Obj->getRelocationSymbol(&Rel, SymTab); + if (Sym && Sym->getType() == ELF::STT_SECTION) { + ErrorOr<const Elf_Shdr *> Sec = Obj->getSection(Sym, SymTab, ShndxTable); + error(Sec.getError()); + ErrorOr<StringRef> SecName = Obj->getSectionName(*Sec); + if (SecName) + TargetName = SecName.get(); + } else if (Sym) { + ErrorOr<StringRef> StrTableOrErr = Obj->getStringTableForSymtab(*SymTab); + error(StrTableOrErr.getError()); + TargetName = errorOrDefault(Sym->getName(*StrTableOrErr)); } if (opts::ExpandRelocs) { @@ -783,27 +1243,38 @@ void ELFDumper<ELFT>::printRelocation(const Elf_Shdr *Sec, } template<class ELFT> +void ELFDumper<ELFT>::printSymbolsHelper(bool IsDynamic) { + const Elf_Shdr *Symtab = (IsDynamic) ? DotDynSymSec : DotSymtabSec; + if (!Symtab) + return; + ErrorOr<StringRef> StrTableOrErr = Obj->getStringTableForSymtab(*Symtab); + error(StrTableOrErr.getError()); + StringRef StrTable = *StrTableOrErr; + for (const Elf_Sym &Sym : Obj->symbols(Symtab)) + printSymbol(&Sym, Symtab, StrTable, IsDynamic); +} + +template<class ELFT> void ELFDumper<ELFT>::printSymbols() { ListScope Group(W, "Symbols"); - for (const typename ELFO::Elf_Sym &Sym : Obj->symbols()) - printSymbol(&Sym, false); + printSymbolsHelper(false); } template<class ELFT> void ELFDumper<ELFT>::printDynamicSymbols() { ListScope Group(W, "DynamicSymbols"); - - for (const typename ELFO::Elf_Sym &Sym : Obj->dynamic_symbols()) - printSymbol(&Sym, true); + printSymbolsHelper(true); } template <class ELFT> -void ELFDumper<ELFT>::printSymbol(const typename ELFO::Elf_Sym *Symbol, - bool IsDynamic) { +void ELFDumper<ELFT>::printSymbol(const Elf_Sym *Symbol, const Elf_Shdr *SymTab, + StringRef StrTable, bool IsDynamic) { unsigned SectionIndex = 0; StringRef SectionName; - getSectionNameIndex(*Obj, Symbol, SectionName, SectionIndex); - std::string FullSymbolName = getFullSymbolName(*Obj, Symbol, IsDynamic); + getSectionNameIndex(*Obj, Symbol, SymTab, ShndxTable, SectionName, + SectionIndex); + std::string FullSymbolName = getFullSymbolName(Symbol, StrTable, IsDynamic); + unsigned char SymbolType = Symbol->getType(); DictScope D(W, "Symbol"); W.printNumber("Name", FullSymbolName, Symbol->st_name); @@ -811,7 +1282,11 @@ void ELFDumper<ELFT>::printSymbol(const typename ELFO::Elf_Sym *Symbol, W.printNumber("Size", Symbol->st_size); W.printEnum ("Binding", Symbol->getBinding(), makeArrayRef(ElfSymbolBindings)); - W.printEnum ("Type", Symbol->getType(), makeArrayRef(ElfSymbolTypes)); + if (Obj->getHeader()->e_machine == ELF::EM_AMDGPU && + SymbolType >= ELF::STT_LOOS && SymbolType < ELF::STT_HIOS) + W.printEnum ("Type", SymbolType, makeArrayRef(AMDGPUSymbolTypes)); + else + W.printEnum ("Type", SymbolType, makeArrayRef(ElfSymbolTypes)); W.printNumber("Other", Symbol->st_other); W.printHex("Section", SectionName, SectionIndex); } @@ -855,12 +1330,15 @@ static const char *getTypeString(uint64_t Type) { LLVM_READOBJ_TYPE_CASE(SYMENT); LLVM_READOBJ_TYPE_CASE(SYMTAB); LLVM_READOBJ_TYPE_CASE(TEXTREL); + LLVM_READOBJ_TYPE_CASE(VERDEF); + LLVM_READOBJ_TYPE_CASE(VERDEFNUM); LLVM_READOBJ_TYPE_CASE(VERNEED); LLVM_READOBJ_TYPE_CASE(VERNEEDNUM); LLVM_READOBJ_TYPE_CASE(VERSYM); LLVM_READOBJ_TYPE_CASE(RELCOUNT); LLVM_READOBJ_TYPE_CASE(GNU_HASH); LLVM_READOBJ_TYPE_CASE(MIPS_RLD_VERSION); + LLVM_READOBJ_TYPE_CASE(MIPS_RLD_MAP_REL); LLVM_READOBJ_TYPE_CASE(MIPS_FLAGS); LLVM_READOBJ_TYPE_CASE(MIPS_BASE_ADDRESS); LLVM_READOBJ_TYPE_CASE(MIPS_LOCAL_GOTNO); @@ -956,8 +1434,15 @@ void printFlags(T Value, ArrayRef<EnumEntry<TFlag>> Flags, raw_ostream &OS) { } template <class ELFT> -static void printValue(const ELFFile<ELFT> *O, uint64_t Type, uint64_t Value, - bool Is64, raw_ostream &OS) { +StringRef ELFDumper<ELFT>::getDynamicString(uint64_t Value) const { + if (Value >= DynamicStringTable.size()) + reportError("Invalid dynamic string table reference"); + return StringRef(DynamicStringTable.data() + Value); +} + +template <class ELFT> +void ELFDumper<ELFT>::printValue(uint64_t Type, uint64_t Value) { + raw_ostream &OS = W.getOStream(); switch (Type) { case DT_PLTREL: if (Value == DT_REL) { @@ -981,6 +1466,7 @@ static void printValue(const ELFFile<ELFT> *O, uint64_t Type, uint64_t Value, case DT_FINI_ARRAY: case DT_PREINIT_ARRAY: case DT_DEBUG: + case DT_VERDEF: case DT_VERNEED: case DT_VERSYM: case DT_GNU_HASH: @@ -988,11 +1474,13 @@ static void printValue(const ELFFile<ELFT> *O, uint64_t Type, uint64_t Value, case DT_MIPS_BASE_ADDRESS: case DT_MIPS_GOTSYM: case DT_MIPS_RLD_MAP: + case DT_MIPS_RLD_MAP_REL: case DT_MIPS_PLTGOT: case DT_MIPS_OPTIONS: OS << format("0x%" PRIX64, Value); break; case DT_RELCOUNT: + case DT_VERDEFNUM: case DT_VERNEEDNUM: case DT_MIPS_RLD_VERSION: case DT_MIPS_LOCAL_GOTNO: @@ -1013,14 +1501,14 @@ static void printValue(const ELFFile<ELFT> *O, uint64_t Type, uint64_t Value, OS << Value << " (bytes)"; break; case DT_NEEDED: - OS << "SharedLibrary (" << O->getDynamicString(Value) << ")"; + OS << "SharedLibrary (" << getDynamicString(Value) << ")"; break; case DT_SONAME: - OS << "LibrarySoname (" << O->getDynamicString(Value) << ")"; + OS << "LibrarySoname (" << getDynamicString(Value) << ")"; break; case DT_RPATH: case DT_RUNPATH: - OS << O->getDynamicString(Value); + OS << getDynamicString(Value); break; case DT_MIPS_FLAGS: printFlags(Value, makeArrayRef(ElfDynamicDTMipsFlags), OS); @@ -1046,7 +1534,8 @@ namespace { template <> void ELFDumper<ELFType<support::little, false>>::printUnwindInfo() { const unsigned Machine = Obj->getHeader()->e_machine; if (Machine == EM_ARM) { - ARM::EHABI::PrinterContext<ELFType<support::little, false>> Ctx(W, Obj); + ARM::EHABI::PrinterContext<ELFType<support::little, false>> Ctx( + W, Obj, DotSymtabSec); return Ctx.PrintUnwindInformation(); } W.startLine() << "UnwindInfo not implemented.\n"; @@ -1055,9 +1544,20 @@ template <> void ELFDumper<ELFType<support::little, false>>::printUnwindInfo() { template<class ELFT> void ELFDumper<ELFT>::printDynamicTable() { - auto DynTable = Obj->dynamic_table(true); + auto I = dynamic_table_begin(); + auto E = dynamic_table_end(); + + if (I == E) + return; - ptrdiff_t Total = std::distance(DynTable.begin(), DynTable.end()); + --E; + while (I != E && E->getTag() == ELF::DT_NULL) + --E; + if (E->getTag() != ELF::DT_NULL) + ++E; + ++E; + + ptrdiff_t Total = std::distance(I, E); if (Total == 0) return; @@ -1069,12 +1569,13 @@ void ELFDumper<ELFT>::printDynamicTable() { W.startLine() << " Tag" << (Is64 ? " " : " ") << "Type" << " " << "Name/Value\n"; - for (const auto &Entry : DynTable) { - W.startLine() - << " " - << format(Is64 ? "0x%016" PRIX64 : "0x%08" PRIX64, Entry.getTag()) - << " " << format("%-21s", getTypeString(Entry.getTag())); - printValue(Obj, Entry.getTag(), Entry.getVal(), Is64, OS); + while (I != E) { + const Elf_Dyn &Entry = *I; + uintX_t Tag = Entry.getTag(); + ++I; + W.startLine() << " " << format_hex(Tag, Is64 ? 18 : 10, true) << " " + << format("%-21s", getTypeString(Tag)); + printValue(Tag, Entry.getVal()); OS << "\n"; } @@ -1088,14 +1589,14 @@ void ELFDumper<ELFT>::printNeededLibraries() { typedef std::vector<StringRef> LibsTy; LibsTy Libs; - for (const auto &Entry : Obj->dynamic_table()) + for (const auto &Entry : dynamic_table()) if (Entry.d_tag == ELF::DT_NEEDED) - Libs.push_back(Obj->getDynamicString(Entry.d_un.d_val)); + Libs.push_back(getDynamicString(Entry.d_un.d_val)); std::stable_sort(Libs.begin(), Libs.end()); - for (LibsTy::const_iterator I = Libs.begin(), E = Libs.end(); I != E; ++I) { - outs() << " " << *I << "\n"; + for (const auto &L : Libs) { + outs() << " " << L << "\n"; } } @@ -1103,33 +1604,51 @@ template<class ELFT> void ELFDumper<ELFT>::printProgramHeaders() { ListScope L(W, "ProgramHeaders"); - for (typename ELFO::Elf_Phdr_Iter PI = Obj->program_header_begin(), - PE = Obj->program_header_end(); - PI != PE; ++PI) { + for (const Elf_Phdr &Phdr : Obj->program_headers()) { DictScope P(W, "ProgramHeader"); - W.printHex ("Type", - getElfSegmentType(Obj->getHeader()->e_machine, PI->p_type), - PI->p_type); - W.printHex ("Offset", PI->p_offset); - W.printHex ("VirtualAddress", PI->p_vaddr); - W.printHex ("PhysicalAddress", PI->p_paddr); - W.printNumber("FileSize", PI->p_filesz); - W.printNumber("MemSize", PI->p_memsz); - W.printFlags ("Flags", PI->p_flags, makeArrayRef(ElfSegmentFlags)); - W.printNumber("Alignment", PI->p_align); + W.printHex("Type", + getElfSegmentType(Obj->getHeader()->e_machine, Phdr.p_type), + Phdr.p_type); + W.printHex("Offset", Phdr.p_offset); + W.printHex("VirtualAddress", Phdr.p_vaddr); + W.printHex("PhysicalAddress", Phdr.p_paddr); + W.printNumber("FileSize", Phdr.p_filesz); + W.printNumber("MemSize", Phdr.p_memsz); + W.printFlags("Flags", Phdr.p_flags, makeArrayRef(ElfSegmentFlags)); + W.printNumber("Alignment", Phdr.p_align); } } template <typename ELFT> void ELFDumper<ELFT>::printHashTable() { DictScope D(W, "HashTable"); - auto HT = Obj->getHashTable(); - if (!HT) + if (!HashTable) return; - W.printNumber("Num Buckets", HT->nbucket); - W.printNumber("Num Chains", HT->nchain); - W.printList("Buckets", HT->buckets()); - W.printList("Chains", HT->chains()); + W.printNumber("Num Buckets", HashTable->nbucket); + W.printNumber("Num Chains", HashTable->nchain); + W.printList("Buckets", HashTable->buckets()); + W.printList("Chains", HashTable->chains()); +} + +template <typename ELFT> +void ELFDumper<ELFT>::printGnuHashTable() { + DictScope D(W, "GnuHashTable"); + if (!GnuHashTable) + return; + W.printNumber("Num Buckets", GnuHashTable->nbuckets); + W.printNumber("First Hashed Symbol Index", GnuHashTable->symndx); + W.printNumber("Num Mask Words", GnuHashTable->maskwords); + W.printNumber("Shift Count", GnuHashTable->shift2); + W.printHexList("Bloom Filter", GnuHashTable->filter()); + W.printList("Buckets", GnuHashTable->buckets()); + if (!DotDynSymSec) + reportError("No dynamic symbol section"); + W.printHexList("Values", + GnuHashTable->values(DotDynSymSec->getEntityCount())); +} + +template <typename ELFT> void ELFDumper<ELFT>::printLoadName() { + outs() << "LoadName: " << SOName << '\n'; } template <class ELFT> @@ -1171,21 +1690,23 @@ template <> void ELFDumper<ELFType<support::little, false>>::printAttributes() { namespace { template <class ELFT> class MipsGOTParser { public: - typedef object::ELFFile<ELFT> ObjectFile; - typedef typename ObjectFile::Elf_Shdr Elf_Shdr; - typedef typename ObjectFile::Elf_Sym Elf_Sym; + typedef object::ELFFile<ELFT> ELFO; + typedef typename ELFO::Elf_Shdr Elf_Shdr; + typedef typename ELFO::Elf_Sym Elf_Sym; + typedef typename ELFO::Elf_Dyn_Range Elf_Dyn_Range; + typedef typename ELFO::Elf_Addr GOTEntry; + typedef typename ELFO::Elf_Rel Elf_Rel; + typedef typename ELFO::Elf_Rela Elf_Rela; - MipsGOTParser(const ObjectFile *Obj, StreamWriter &W); + MipsGOTParser(ELFDumper<ELFT> *Dumper, const ELFO *Obj, + Elf_Dyn_Range DynTable, StreamWriter &W); void parseGOT(); void parsePLT(); private: - typedef typename ObjectFile::Elf_Addr GOTEntry; - typedef typename ObjectFile::template ELFEntityIterator<const GOTEntry> - GOTIter; - - const ObjectFile *Obj; + ELFDumper<ELFT> *Dumper; + const ELFO *Obj; StreamWriter &W; llvm::Optional<uint64_t> DtPltGot; llvm::Optional<uint64_t> DtLocalGotNum; @@ -1194,22 +1715,26 @@ private: llvm::Optional<uint64_t> DtJmpRel; std::size_t getGOTTotal(ArrayRef<uint8_t> GOT) const; - GOTIter makeGOTIter(ArrayRef<uint8_t> GOT, std::size_t EntryNum); + const GOTEntry *makeGOTIter(ArrayRef<uint8_t> GOT, std::size_t EntryNum); - void printGotEntry(uint64_t GotAddr, GOTIter BeginIt, GOTIter It); - void printGlobalGotEntry(uint64_t GotAddr, GOTIter BeginIt, GOTIter It, - const Elf_Sym *Sym, bool IsDynamic); - void printPLTEntry(uint64_t PLTAddr, GOTIter BeginIt, GOTIter It, - StringRef Purpose); - void printPLTEntry(uint64_t PLTAddr, GOTIter BeginIt, GOTIter It, + void printGotEntry(uint64_t GotAddr, const GOTEntry *BeginIt, + const GOTEntry *It); + void printGlobalGotEntry(uint64_t GotAddr, const GOTEntry *BeginIt, + const GOTEntry *It, const Elf_Sym *Sym, + StringRef StrTable, bool IsDynamic); + void printPLTEntry(uint64_t PLTAddr, const GOTEntry *BeginIt, + const GOTEntry *It, StringRef Purpose); + void printPLTEntry(uint64_t PLTAddr, const GOTEntry *BeginIt, + const GOTEntry *It, StringRef StrTable, const Elf_Sym *Sym); }; } template <class ELFT> -MipsGOTParser<ELFT>::MipsGOTParser(const ObjectFile *Obj, StreamWriter &W) - : Obj(Obj), W(W) { - for (const auto &Entry : Obj->dynamic_table()) { +MipsGOTParser<ELFT>::MipsGOTParser(ELFDumper<ELFT> *Dumper, const ELFO *Obj, + Elf_Dyn_Range DynTable, StreamWriter &W) + : Dumper(Dumper), Obj(Obj), W(W) { + for (const auto &Entry : DynTable) { switch (Entry.getTag()) { case ELF::DT_PLTGOT: DtPltGot = Entry.getVal(); @@ -1264,8 +1789,11 @@ template <class ELFT> void MipsGOTParser<ELFT>::parseGOT() { return; } - const Elf_Sym *DynSymBegin = Obj->dynamic_symbol_begin(); - const Elf_Sym *DynSymEnd = Obj->dynamic_symbol_end(); + const Elf_Shdr *DynSymSec = Dumper->getDotDynSymSec(); + ErrorOr<StringRef> StrTable = Obj->getStringTableForSymtab(*DynSymSec); + error(StrTable.getError()); + const Elf_Sym *DynSymBegin = Obj->symbol_begin(DynSymSec); + const Elf_Sym *DynSymEnd = Obj->symbol_end(DynSymSec); std::size_t DynSymTotal = std::size_t(std::distance(DynSymBegin, DynSymEnd)); if (*DtGotSym > DynSymTotal) { @@ -1280,9 +1808,9 @@ template <class ELFT> void MipsGOTParser<ELFT>::parseGOT() { return; } - GOTIter GotBegin = makeGOTIter(*GOT, 0); - GOTIter GotLocalEnd = makeGOTIter(*GOT, *DtLocalGotNum); - GOTIter It = GotBegin; + const GOTEntry *GotBegin = makeGOTIter(*GOT, 0); + const GOTEntry *GotLocalEnd = makeGOTIter(*GOT, *DtLocalGotNum); + const GOTEntry *It = GotBegin; DictScope GS(W, "Primary GOT"); @@ -1312,11 +1840,13 @@ template <class ELFT> void MipsGOTParser<ELFT>::parseGOT() { { ListScope GS(W, "Global entries"); - GOTIter GotGlobalEnd = makeGOTIter(*GOT, *DtLocalGotNum + GlobalGotNum); + const GOTEntry *GotGlobalEnd = + makeGOTIter(*GOT, *DtLocalGotNum + GlobalGotNum); const Elf_Sym *GotDynSym = DynSymBegin + *DtGotSym; for (; It != GotGlobalEnd; ++It) { DictScope D(W, "Entry"); - printGlobalGotEntry(GOTShdr->sh_addr, GotBegin, It, GotDynSym++, true); + printGlobalGotEntry(GOTShdr->sh_addr, GotBegin, It, GotDynSym++, + *StrTable, true); } } @@ -1350,10 +1880,16 @@ template <class ELFT> void MipsGOTParser<ELFT>::parsePLT() { W.startLine() << "There is no .rel.plt section in the file.\n"; return; } + ErrorOr<const Elf_Shdr *> SymTableOrErr = + Obj->getSection(PLTRelShdr->sh_link); + error(SymTableOrErr.getError()); + const Elf_Shdr *SymTable = *SymTableOrErr; + ErrorOr<StringRef> StrTable = Obj->getStringTableForSymtab(*SymTable); + error(StrTable.getError()); - GOTIter PLTBegin = makeGOTIter(*PLT, 0); - GOTIter PLTEnd = makeGOTIter(*PLT, getGOTTotal(*PLT)); - GOTIter It = PLTBegin; + const GOTEntry *PLTBegin = makeGOTIter(*PLT, 0); + const GOTEntry *PLTEnd = makeGOTIter(*PLT, getGOTTotal(*PLT)); + const GOTEntry *It = PLTBegin; DictScope GS(W, "PLT GOT"); { @@ -1367,21 +1903,19 @@ template <class ELFT> void MipsGOTParser<ELFT>::parsePLT() { switch (PLTRelShdr->sh_type) { case ELF::SHT_REL: - for (typename ObjectFile::Elf_Rel_Iter RI = Obj->rel_begin(PLTRelShdr), - RE = Obj->rel_end(PLTRelShdr); + for (const Elf_Rel *RI = Obj->rel_begin(PLTRelShdr), + *RE = Obj->rel_end(PLTRelShdr); RI != RE && It != PLTEnd; ++RI, ++It) { - const Elf_Sym *Sym = - Obj->getRelocationSymbol(&*PLTRelShdr, &*RI).second; - printPLTEntry(PLTShdr->sh_addr, PLTBegin, It, Sym); + const Elf_Sym *Sym = Obj->getRelocationSymbol(&*RI, SymTable); + printPLTEntry(PLTShdr->sh_addr, PLTBegin, It, *StrTable, Sym); } break; case ELF::SHT_RELA: - for (typename ObjectFile::Elf_Rela_Iter RI = Obj->rela_begin(PLTRelShdr), - RE = Obj->rela_end(PLTRelShdr); + for (const Elf_Rela *RI = Obj->rela_begin(PLTRelShdr), + *RE = Obj->rela_end(PLTRelShdr); RI != RE && It != PLTEnd; ++RI, ++It) { - const Elf_Sym *Sym = - Obj->getRelocationSymbol(&*PLTRelShdr, &*RI).second; - printPLTEntry(PLTShdr->sh_addr, PLTBegin, It, Sym); + const Elf_Sym *Sym = Obj->getRelocationSymbol(&*RI, SymTable); + printPLTEntry(PLTShdr->sh_addr, PLTBegin, It, *StrTable, Sym); } break; } @@ -1394,15 +1928,16 @@ std::size_t MipsGOTParser<ELFT>::getGOTTotal(ArrayRef<uint8_t> GOT) const { } template <class ELFT> -typename MipsGOTParser<ELFT>::GOTIter +const typename MipsGOTParser<ELFT>::GOTEntry * MipsGOTParser<ELFT>::makeGOTIter(ArrayRef<uint8_t> GOT, std::size_t EntryNum) { const char *Data = reinterpret_cast<const char *>(GOT.data()); - return GOTIter(sizeof(GOTEntry), Data + EntryNum * sizeof(GOTEntry)); + return reinterpret_cast<const GOTEntry *>(Data + EntryNum * sizeof(GOTEntry)); } template <class ELFT> -void MipsGOTParser<ELFT>::printGotEntry(uint64_t GotAddr, GOTIter BeginIt, - GOTIter It) { +void MipsGOTParser<ELFT>::printGotEntry(uint64_t GotAddr, + const GOTEntry *BeginIt, + const GOTEntry *It) { int64_t Offset = std::distance(BeginIt, It) * sizeof(GOTEntry); W.printHex("Address", GotAddr + Offset); W.printNumber("Access", Offset - 0x7ff0); @@ -1410,9 +1945,9 @@ void MipsGOTParser<ELFT>::printGotEntry(uint64_t GotAddr, GOTIter BeginIt, } template <class ELFT> -void MipsGOTParser<ELFT>::printGlobalGotEntry(uint64_t GotAddr, GOTIter BeginIt, - GOTIter It, const Elf_Sym *Sym, - bool IsDynamic) { +void MipsGOTParser<ELFT>::printGlobalGotEntry( + uint64_t GotAddr, const GOTEntry *BeginIt, const GOTEntry *It, + const Elf_Sym *Sym, StringRef StrTable, bool IsDynamic) { printGotEntry(GotAddr, BeginIt, It); W.printHex("Value", Sym->st_value); @@ -1420,16 +1955,19 @@ void MipsGOTParser<ELFT>::printGlobalGotEntry(uint64_t GotAddr, GOTIter BeginIt, unsigned SectionIndex = 0; StringRef SectionName; - getSectionNameIndex(*Obj, Sym, SectionName, SectionIndex); + getSectionNameIndex(*Obj, Sym, Dumper->getDotDynSymSec(), + Dumper->getShndxTable(), SectionName, SectionIndex); W.printHex("Section", SectionName, SectionIndex); - std::string FullSymbolName = getFullSymbolName(*Obj, Sym, IsDynamic); + std::string FullSymbolName = + Dumper->getFullSymbolName(Sym, StrTable, IsDynamic); W.printNumber("Name", FullSymbolName, Sym->st_name); } template <class ELFT> -void MipsGOTParser<ELFT>::printPLTEntry(uint64_t PLTAddr, GOTIter BeginIt, - GOTIter It, StringRef Purpose) { +void MipsGOTParser<ELFT>::printPLTEntry(uint64_t PLTAddr, + const GOTEntry *BeginIt, + const GOTEntry *It, StringRef Purpose) { DictScope D(W, "Entry"); int64_t Offset = std::distance(BeginIt, It) * sizeof(GOTEntry); W.printHex("Address", PLTAddr + Offset); @@ -1438,8 +1976,10 @@ void MipsGOTParser<ELFT>::printPLTEntry(uint64_t PLTAddr, GOTIter BeginIt, } template <class ELFT> -void MipsGOTParser<ELFT>::printPLTEntry(uint64_t PLTAddr, GOTIter BeginIt, - GOTIter It, const Elf_Sym *Sym) { +void MipsGOTParser<ELFT>::printPLTEntry(uint64_t PLTAddr, + const GOTEntry *BeginIt, + const GOTEntry *It, StringRef StrTable, + const Elf_Sym *Sym) { DictScope D(W, "Entry"); int64_t Offset = std::distance(BeginIt, It) * sizeof(GOTEntry); W.printHex("Address", PLTAddr + Offset); @@ -1449,10 +1989,11 @@ void MipsGOTParser<ELFT>::printPLTEntry(uint64_t PLTAddr, GOTIter BeginIt, unsigned SectionIndex = 0; StringRef SectionName; - getSectionNameIndex(*Obj, Sym, SectionName, SectionIndex); + getSectionNameIndex(*Obj, Sym, Dumper->getDotDynSymSec(), + Dumper->getShndxTable(), SectionName, SectionIndex); W.printHex("Section", SectionName, SectionIndex); - std::string FullSymbolName = getFullSymbolName(*Obj, Sym, true); + std::string FullSymbolName = Dumper->getFullSymbolName(Sym, StrTable, true); W.printNumber("Name", FullSymbolName, Sym->st_name); } @@ -1462,7 +2003,7 @@ template <class ELFT> void ELFDumper<ELFT>::printMipsPLTGOT() { return; } - MipsGOTParser<ELFT> GOTParser(Obj, W); + MipsGOTParser<ELFT> GOTParser(this, Obj, dynamic_table(), W); GOTParser.parseGOT(); GOTParser.parsePLT(); } @@ -1604,7 +2145,7 @@ template <class ELFT> void ELFDumper<ELFT>::printMipsReginfo() { } template <class ELFT> void ELFDumper<ELFT>::printStackMap() const { - const typename ELFFile<ELFT>::Elf_Shdr *StackMapSection = nullptr; + const Elf_Shdr *StackMapSection = nullptr; for (const auto &Sec : Obj->sections()) { ErrorOr<StringRef> Name = Obj->getSectionName(&Sec); if (*Name == ".llvm_stackmaps") { diff --git a/tools/llvm-readobj/MachODumper.cpp b/tools/llvm-readobj/MachODumper.cpp index adb99b0acd7f..58d2c9fca47d 100644 --- a/tools/llvm-readobj/MachODumper.cpp +++ b/tools/llvm-readobj/MachODumper.cpp @@ -40,6 +40,14 @@ public: void printUnwindInfo() override; void printStackMap() const override; + // MachO-specific. + void printMachODataInCode() override; + void printMachOVersionMin() override; + void printMachODysymtab() override; + void printMachOSegment() override; + void printMachOIndirectSymbols() override; + void printMachOLinkerOptions () override; + private: template<class MachHeader> void printFileHeaders(const MachHeader &Header); @@ -255,6 +263,21 @@ namespace { uint32_t Flags; uint32_t Reserved1; uint32_t Reserved2; + uint32_t Reserved3; + }; + + struct MachOSegment { + std::string CmdName; + std::string SegName; + uint64_t cmdsize; + uint64_t vmaddr; + uint64_t vmsize; + uint64_t fileoff; + uint64_t filesize; + uint32_t maxprot; + uint32_t initprot; + uint32_t nsects; + uint32_t flags; }; struct MachOSymbol { @@ -266,6 +289,18 @@ namespace { }; } +static std::string getMask(uint32_t prot) +{ + // TODO (davide): This always assumes prot is valid. + // Catch mistakes and report if needed. + std::string Prot; + Prot = ""; + Prot += (prot & MachO::VM_PROT_READ) ? "r" : "-"; + Prot += (prot & MachO::VM_PROT_WRITE) ? "w" : "-"; + Prot += (prot & MachO::VM_PROT_EXECUTE) ? "x" : "-"; + return Prot; +} + static void getSection(const MachOObjectFile *Obj, DataRefImpl Sec, MachOSection &Section) { @@ -292,8 +327,40 @@ static void getSection(const MachOObjectFile *Obj, Section.Flags = Sect.flags; Section.Reserved1 = Sect.reserved1; Section.Reserved2 = Sect.reserved2; + Section.Reserved3 = Sect.reserved3; } +static void getSegment(const MachOObjectFile *Obj, + const MachOObjectFile::LoadCommandInfo &L, + MachOSegment &Segment) { + if (!Obj->is64Bit()) { + MachO::segment_command SC = Obj->getSegmentLoadCommand(L); + Segment.CmdName = "LC_SEGMENT"; + Segment.SegName = SC.segname; + Segment.cmdsize = SC.cmdsize; + Segment.vmaddr = SC.vmaddr; + Segment.vmsize = SC.vmsize; + Segment.fileoff = SC.fileoff; + Segment.filesize = SC.filesize; + Segment.maxprot = SC.maxprot; + Segment.initprot = SC.initprot; + Segment.nsects = SC.nsects; + Segment.flags = SC.flags; + return; + } + MachO::segment_command_64 SC = Obj->getSegment64LoadCommand(L); + Segment.CmdName = "LC_SEGMENT_64"; + Segment.SegName = SC.segname; + Segment.cmdsize = SC.cmdsize; + Segment.vmaddr = SC.vmaddr; + Segment.vmsize = SC.vmsize; + Segment.fileoff = SC.fileoff; + Segment.filesize = SC.filesize; + Segment.maxprot = SC.maxprot; + Segment.initprot = SC.initprot; + Segment.nsects = SC.nsects; + Segment.flags = SC.flags; +} static void getSymbol(const MachOObjectFile *Obj, DataRefImpl DRI, @@ -375,8 +442,7 @@ void MachODumper::printSections(const MachOObjectFile *Obj) { DataRefImpl DR = Section.getRawDataRefImpl(); StringRef Name; - if (error(Section.getName(Name))) - Name = ""; + error(Section.getName(Name)); ArrayRef<char> RawName = Obj->getSectionRawName(DR); StringRef SegmentName = Obj->getSectionFinalSegmentName(DR); @@ -398,6 +464,8 @@ void MachODumper::printSections(const MachOObjectFile *Obj) { makeArrayRef(MachOSectionAttributes)); W.printHex("Reserved1", MOSection.Reserved1); W.printHex("Reserved2", MOSection.Reserved2); + if (Obj->is64Bit()) + W.printHex("Reserved3", MOSection.Reserved3); if (opts::SectionRelocations) { ListScope D(W, "Relocations"); @@ -419,8 +487,7 @@ void MachODumper::printSections(const MachOObjectFile *Obj) { bool IsBSS = Section.isBSS(); if (!IsBSS) { StringRef Data; - if (error(Section.getContents(Data))) - break; + error(Section.getContents(Data)); W.printBinaryBlock("SectionData", Data); } @@ -434,8 +501,7 @@ void MachODumper::printRelocations() { std::error_code EC; for (const SectionRef &Section : Obj->sections()) { StringRef Name; - if (error(Section.getName(Name))) - continue; + error(Section.getName(Name)); bool PrintedGroup = false; for (const RelocationRef &Reloc : Section.relocations()) { @@ -475,15 +541,13 @@ void MachODumper::printRelocation(const MachOObjectFile *Obj, symbol_iterator Symbol = Reloc.getSymbol(); if (Symbol != Obj->symbol_end()) { ErrorOr<StringRef> TargetNameOrErr = Symbol->getName(); - if (error(TargetNameOrErr.getError())) - return; + error(TargetNameOrErr.getError()); TargetName = *TargetNameOrErr; } } else if (!IsScattered) { section_iterator SecI = Obj->getRelocationSection(DR); if (SecI != Obj->section_end()) { - if (error(SecI->getName(TargetName))) - return; + error(SecI->getName(TargetName)); } } if (TargetName.empty()) @@ -547,8 +611,10 @@ void MachODumper::printSymbol(const SymbolRef &Symbol) { getSymbol(Obj, Symbol.getRawDataRefImpl(), MOSymbol); StringRef SectionName = ""; - section_iterator SecI(Obj->section_begin()); - if (!error(Symbol.getSection(SecI)) && SecI != Obj->section_end()) + ErrorOr<section_iterator> SecIOrErr = Symbol.getSection(); + error(SecIOrErr.getError()); + section_iterator SecI = *SecIOrErr; + if (SecI != Obj->section_end()) error(SecI->getName(SectionName)); DictScope D(W, "Symbol"); @@ -603,3 +669,153 @@ void MachODumper::printStackMap() const { prettyPrintStackMap(llvm::outs(), StackMapV1Parser<support::big>(StackMapContentsArray)); } + +void MachODumper::printMachODataInCode() { + for (const auto &Load : Obj->load_commands()) { + if (Load.C.cmd == MachO::LC_DATA_IN_CODE) { + MachO::linkedit_data_command LLC = Obj->getLinkeditDataLoadCommand(Load); + DictScope Group(W, "DataInCode"); + W.printNumber("Data offset", LLC.dataoff); + W.printNumber("Data size", LLC.datasize); + ListScope D(W, "Data entries"); + unsigned NumRegions = LLC.datasize / sizeof(MachO::data_in_code_entry); + for (unsigned i = 0; i < NumRegions; ++i) { + MachO::data_in_code_entry DICE = Obj->getDataInCodeTableEntry( + LLC.dataoff, i); + DictScope Group(W, "Entry"); + W.printNumber("Index", i); + W.printNumber("Offset", DICE.offset); + W.printNumber("Length", DICE.length); + W.printNumber("Kind", DICE.kind); + } + } + } +} + +void MachODumper::printMachOVersionMin() { + for (const auto &Load : Obj->load_commands()) { + StringRef Cmd; + switch (Load.C.cmd) { + case MachO::LC_VERSION_MIN_MACOSX: + Cmd = "LC_VERSION_MIN_MACOSX"; + break; + case MachO::LC_VERSION_MIN_IPHONEOS: + Cmd = "LC_VERSION_MIN_IPHONEOS"; + break; + case MachO::LC_VERSION_MIN_TVOS: + Cmd = "LC_VERSION_MIN_TVOS"; + break; + case MachO::LC_VERSION_MIN_WATCHOS: + Cmd = "LC_VERSION_MIN_WATCHOS"; + break; + default: + continue; + } + + MachO::version_min_command VMC = Obj->getVersionMinLoadCommand(Load); + DictScope Group(W, "MinVersion"); + W.printString("Cmd", Cmd); + W.printNumber("Size", VMC.cmdsize); + SmallString<32> Version; + Version = utostr(MachOObjectFile::getVersionMinMajor(VMC, false)) + "." + + utostr(MachOObjectFile::getVersionMinMinor(VMC, false)); + uint32_t Update = MachOObjectFile::getVersionMinUpdate(VMC, false); + if (Update != 0) + Version += "." + utostr(MachOObjectFile::getVersionMinUpdate(VMC, false)); + W.printString("Version", Version); + SmallString<32> SDK; + if (VMC.sdk == 0) + SDK = "n/a"; + else { + SDK = utostr(MachOObjectFile::getVersionMinMajor(VMC, true)) + "." + + utostr(MachOObjectFile::getVersionMinMinor(VMC, true)); + uint32_t Update = MachOObjectFile::getVersionMinUpdate(VMC, true); + if (Update != 0) + SDK += "." + utostr(MachOObjectFile::getVersionMinUpdate(VMC, true)); + } + W.printString("SDK", SDK); + } +} + +void MachODumper::printMachODysymtab() { + for (const auto &Load : Obj->load_commands()) { + if (Load.C.cmd == MachO::LC_DYSYMTAB) { + MachO::dysymtab_command DLC = Obj->getDysymtabLoadCommand(); + DictScope Group(W, "Dysymtab"); + W.printNumber("ilocalsym", DLC.ilocalsym); + W.printNumber("nlocalsym", DLC.nlocalsym); + W.printNumber("iextdefsym", DLC.iextdefsym); + W.printNumber("nextdefsym", DLC.nextdefsym); + W.printNumber("iundefsym", DLC.iundefsym); + W.printNumber("nundefsym", DLC.nundefsym); + W.printNumber("tocoff", DLC.tocoff); + W.printNumber("ntoc", DLC.ntoc); + W.printNumber("modtaboff", DLC.modtaboff); + W.printNumber("nmodtab", DLC.nmodtab); + W.printNumber("extrefsymoff", DLC.extrefsymoff); + W.printNumber("nextrefsyms", DLC.nextrefsyms); + W.printNumber("indirectsymoff", DLC.indirectsymoff); + W.printNumber("nindirectsyms", DLC.nindirectsyms); + W.printNumber("extreloff", DLC.extreloff); + W.printNumber("nextrel", DLC.nextrel); + W.printNumber("locreloff", DLC.locreloff); + W.printNumber("nlocrel", DLC.nlocrel); + } + } +} + +void MachODumper::printMachOSegment() { + for (const auto &Load : Obj->load_commands()) { + if (Load.C.cmd == MachO::LC_SEGMENT || Load.C.cmd == MachO::LC_SEGMENT_64) { + MachOSegment MOSegment; + getSegment(Obj, Load, MOSegment); + DictScope Group(W, "Segment"); + W.printString("Cmd", MOSegment.CmdName); + W.printString("Name", MOSegment.SegName); + W.printNumber("Size", MOSegment.cmdsize); + W.printHex("vmaddr", MOSegment.vmaddr); + W.printHex("vmsize", MOSegment.vmsize); + W.printNumber("fileoff", MOSegment.fileoff); + W.printNumber("filesize", MOSegment.filesize); + W.printString("maxprot", getMask(MOSegment.maxprot)); + W.printString("initprot", getMask(MOSegment.initprot)); + W.printNumber("nsects", MOSegment.nsects); + W.printHex("flags", MOSegment.flags); + } + } +} + +void MachODumper::printMachOIndirectSymbols() { + for (const auto &Load : Obj->load_commands()) { + if (Load.C.cmd == MachO::LC_DYSYMTAB) { + MachO::dysymtab_command DLC = Obj->getDysymtabLoadCommand(); + DictScope Group(W, "Indirect Symbols"); + W.printNumber("Number", DLC.nindirectsyms); + ListScope D(W, "Symbols"); + for (unsigned i = 0; i < DLC.nindirectsyms; ++i) { + DictScope Group(W, "Entry"); + W.printNumber("Entry Index", i); + W.printHex("Symbol Index", Obj->getIndirectSymbolTableEntry(DLC, i)); + } + } + } +} + +void MachODumper::printMachOLinkerOptions() { + for (const auto &Load : Obj->load_commands()) { + if (Load.C.cmd == MachO::LC_LINKER_OPTION) { + MachO::linker_option_command LOLC = Obj->getLinkerOptionLoadCommand(Load); + DictScope Group(W, "Linker Options"); + W.printNumber("Size", LOLC.cmdsize); + ListScope D(W, "Strings"); + uint64_t DataSize = LOLC.cmdsize - sizeof(MachO::linker_option_command); + const char *P = Load.Ptr + sizeof(MachO::linker_option_command); + StringRef Data(P, DataSize); + for (unsigned i = 0; i < LOLC.count; ++i) { + std::pair<StringRef,StringRef> Split = Data.split('\0'); + W.printString("Value", Split.first); + Data = Split.second; + } + } + } +} diff --git a/tools/llvm-readobj/ObjDumper.h b/tools/llvm-readobj/ObjDumper.h index 5ecf0ec3d6fa..db26d6983552 100644 --- a/tools/llvm-readobj/ObjDumper.h +++ b/tools/llvm-readobj/ObjDumper.h @@ -1,4 +1,4 @@ -//===-- ObjDumper.h -------------------------------------------------------===// +//===-- ObjDumper.h ---------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -15,7 +15,8 @@ namespace llvm { namespace object { - class ObjectFile; +class COFFImportFile; +class ObjectFile; } class StreamWriter; @@ -38,6 +39,9 @@ public: virtual void printNeededLibraries() { } virtual void printProgramHeaders() { } virtual void printHashTable() { } + virtual void printGnuHashTable() { } + virtual void printLoadName() {} + virtual void printVersionInfo() {} // Only implemented for ARM ELF at this time. virtual void printAttributes() { } @@ -52,6 +56,15 @@ public: virtual void printCOFFExports() { } virtual void printCOFFDirectives() { } virtual void printCOFFBaseReloc() { } + virtual void printCodeViewDebugInfo() { } + + // Only implemented for MachO. + virtual void printMachODataInCode() { } + virtual void printMachOVersionMin() { } + virtual void printMachODysymtab() { } + virtual void printMachOSegment() { } + virtual void printMachOIndirectSymbols() { } + virtual void printMachOLinkerOptions() { } virtual void printStackMap() const = 0; @@ -71,6 +84,8 @@ std::error_code createMachODumper(const object::ObjectFile *Obj, StreamWriter &Writer, std::unique_ptr<ObjDumper> &Result); +void dumpCOFFImportFile(const object::COFFImportFile *File); + } // namespace llvm #endif diff --git a/tools/llvm-readobj/StreamWriter.h b/tools/llvm-readobj/StreamWriter.h index f3cc57ef940e..d2dbb07af256 100644 --- a/tools/llvm-readobj/StreamWriter.h +++ b/tools/llvm-readobj/StreamWriter.h @@ -34,14 +34,17 @@ struct HexNumber { // unsigned type. The overloads are here so that every type that is implicitly // convertible to an integer (including enums and endian helpers) can be used // without requiring type traits or call-site changes. - HexNumber(int8_t Value) : Value(static_cast<uint8_t >(Value)) { } - HexNumber(int16_t Value) : Value(static_cast<uint16_t>(Value)) { } - HexNumber(int32_t Value) : Value(static_cast<uint32_t>(Value)) { } - HexNumber(int64_t Value) : Value(static_cast<uint64_t>(Value)) { } - HexNumber(uint8_t Value) : Value(Value) { } - HexNumber(uint16_t Value) : Value(Value) { } - HexNumber(uint32_t Value) : Value(Value) { } - HexNumber(uint64_t Value) : Value(Value) { } + HexNumber(char Value) : Value(static_cast<unsigned char>(Value)) { } + HexNumber(signed char Value) : Value(static_cast<unsigned char>(Value)) { } + HexNumber(signed short Value) : Value(static_cast<unsigned short>(Value)) { } + HexNumber(signed int Value) : Value(static_cast<unsigned int>(Value)) { } + HexNumber(signed long Value) : Value(static_cast<unsigned long>(Value)) { } + HexNumber(signed long long Value) : Value(static_cast<unsigned long long>(Value)) { } + HexNumber(unsigned char Value) : Value(Value) { } + HexNumber(unsigned short Value) : Value(Value) { } + HexNumber(unsigned int Value) : Value(Value) { } + HexNumber(unsigned long Value) : Value(Value) { } + HexNumber(unsigned long long Value) : Value(Value) { } uint64_t Value; }; @@ -194,6 +197,19 @@ public: OS << "]\n"; } + template <typename T> + void printHexList(StringRef Label, const T &List) { + startLine() << Label << ": ["; + bool Comma = false; + for (const auto &Item : List) { + if (Comma) + OS << ", "; + OS << hex(Item); + Comma = true; + } + OS << "]\n"; + } + template<typename T> void printHex(StringRef Label, T Value) { startLine() << Label << ": " << hex(Value) << "\n"; diff --git a/tools/llvm-readobj/Win64EHDumper.cpp b/tools/llvm-readobj/Win64EHDumper.cpp index f57eea20e2d9..2da5ae3200fd 100644 --- a/tools/llvm-readobj/Win64EHDumper.cpp +++ b/tools/llvm-readobj/Win64EHDumper.cpp @@ -149,11 +149,8 @@ static std::error_code resolveRelocation(const Dumper::Context &Ctx, return EC; ResolvedAddress = *ResolvedAddressOrErr; - section_iterator SI = Ctx.COFF.section_begin(); - if (std::error_code EC = Symbol.getSection(SI)) - return EC; - - ResolvedSection = Ctx.COFF.getCOFFSection(*SI); + ErrorOr<section_iterator> SI = Symbol.getSection(); + ResolvedSection = Ctx.COFF.getCOFFSection(**SI); return std::error_code(); } @@ -257,7 +254,7 @@ void Dumper::printUnwindInfo(const Context &Ctx, const coff_section *Section, return; } - printUnwindCode(UI, ArrayRef<UnwindCode>(UCI, UCE)); + printUnwindCode(UI, makeArrayRef(UCI, UCE)); UCI = UCI + UsedSlots - 1; } } @@ -284,11 +281,11 @@ void Dumper::printRuntimeFunction(const Context &Ctx, const coff_section *XData; uint64_t Offset; - if (error(resolveRelocation(Ctx, Section, SectionOffset + 8, XData, Offset))) - return; + resolveRelocation(Ctx, Section, SectionOffset + 8, XData, Offset); ArrayRef<uint8_t> Contents; - if (error(Ctx.COFF.getSectionContents(XData, Contents)) || Contents.empty()) + error(Ctx.COFF.getSectionContents(XData, Contents)); + if (Contents.empty()) return; Offset = Offset + RF.UnwindInfoOffset; @@ -302,15 +299,15 @@ void Dumper::printRuntimeFunction(const Context &Ctx, void Dumper::printData(const Context &Ctx) { for (const auto &Section : Ctx.COFF.sections()) { StringRef Name; - if (error(Section.getName(Name))) - continue; + Section.getName(Name); if (Name != ".pdata" && !Name.startswith(".pdata$")) continue; const coff_section *PData = Ctx.COFF.getCOFFSection(Section); ArrayRef<uint8_t> Contents; - if (error(Ctx.COFF.getSectionContents(PData, Contents)) || Contents.empty()) + error(Ctx.COFF.getSectionContents(PData, Contents)); + if (Contents.empty()) continue; const RuntimeFunction *Entries = diff --git a/tools/llvm-readobj/llvm-readobj.cpp b/tools/llvm-readobj/llvm-readobj.cpp index 12afacb0a858..fa8fee2b03ab 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/COFFImportFile.h" #include "llvm/Object/ELFObjectFile.h" #include "llvm/Object/MachOUniversal.h" #include "llvm/Object/ObjectFile.h" @@ -131,6 +132,10 @@ namespace opts { cl::opt<bool> HashTable("hash-table", cl::desc("Display ELF hash table")); + // -gnu-hash-table + cl::opt<bool> GnuHashTable("gnu-hash-table", + cl::desc("Display ELF .gnu.hash section")); + // -expand-relocs cl::opt<bool> ExpandRelocs("expand-relocs", cl::desc("Expand each shown relocation to multiple lines")); @@ -181,25 +186,62 @@ namespace opts { COFFBaseRelocs("coff-basereloc", cl::desc("Display the PE/COFF .reloc section")); + // -macho-data-in-code + cl::opt<bool> + MachODataInCode("macho-data-in-code", + cl::desc("Display MachO Data in Code command")); + + // -macho-indirect-symbols + cl::opt<bool> + MachOIndirectSymbols("macho-indirect-symbols", + cl::desc("Display MachO indirect symbols")); + + // -macho-linker-options + cl::opt<bool> + MachOLinkerOptions("macho-linker-options", + cl::desc("Display MachO linker options")); + + // -macho-segment + cl::opt<bool> + MachOSegment("macho-segment", + cl::desc("Display MachO Segment command")); + + // -macho-version-min + cl::opt<bool> + MachOVersionMin("macho-version-min", + cl::desc("Display MachO version min command")); + + // -macho-dysymtab + cl::opt<bool> + MachODysymtab("macho-dysymtab", + cl::desc("Display MachO Dysymtab command")); + // -stackmap cl::opt<bool> PrintStackMap("stackmap", cl::desc("Display contents of stackmap section")); + // -version-info + cl::opt<bool> + VersionInfo("version-info", + cl::desc("Display ELF version sections (if present)")); + cl::alias VersionInfoShort("V", cl::desc("Alias for -version-info"), + cl::aliasopt(VersionInfo)); } // namespace opts -static int ReturnValue = EXIT_SUCCESS; - namespace llvm { -bool error(std::error_code EC) { +LLVM_ATTRIBUTE_NORETURN void reportError(Twine Msg) { + errs() << "\nError reading file: " << Msg << ".\n"; + errs().flush(); + exit(1); +} + +void error(std::error_code EC) { if (!EC) - return false; + return; - ReturnValue = EXIT_FAILURE; - outs() << "\nError reading file: " << EC.message() << ".\n"; - outs().flush(); - return true; + reportError(EC.message()); } bool relocAddressLess(RelocationRef a, RelocationRef b) { @@ -212,17 +254,14 @@ static void reportError(StringRef Input, std::error_code EC) { if (Input == "-") Input = "<stdin>"; - errs() << Input << ": " << EC.message() << "\n"; - errs().flush(); - ReturnValue = EXIT_FAILURE; + reportError(Twine(Input) + ": " + EC.message()); } static void reportError(StringRef Input, StringRef Message) { if (Input == "-") Input = "<stdin>"; - errs() << Input << ": " << Message << "\n"; - ReturnValue = EXIT_FAILURE; + reportError(Twine(Input) + ": " + Message); } static bool isMipsArch(unsigned Arch) { @@ -253,26 +292,12 @@ 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) { StreamWriter Writer(outs()); std::unique_ptr<ObjDumper> Dumper; - if (std::error_code EC = createDumper(Obj, Writer, Dumper)) { + if (std::error_code EC = createDumper(Obj, Writer, Dumper)) reportError(Obj->getFileName(), EC); - return; - } outs() << '\n'; outs() << "File: " << Obj->getFileName() << "\n"; @@ -281,8 +306,7 @@ static void dumpObject(const ObjectFile *Obj) { << Triple::getArchTypeName((llvm::Triple::ArchType)Obj->getArch()) << "\n"; outs() << "AddressSize: " << (8*Obj->getBytesInAddress()) << "bit\n"; - if (Obj->isELF()) - outs() << "LoadName: " << getLoadName(Obj) << "\n"; + Dumper->printLoadName(); if (opts::FileHeaders) Dumper->printFileHeaders(); @@ -306,6 +330,10 @@ static void dumpObject(const ObjectFile *Obj) { Dumper->printProgramHeaders(); if (opts::HashTable) Dumper->printHashTable(); + if (opts::GnuHashTable) + Dumper->printGnuHashTable(); + if (opts::VersionInfo) + Dumper->printVersionInfo(); if (Obj->getArch() == llvm::Triple::arm && Obj->isELF()) if (opts::ARMAttributes) Dumper->printAttributes(); @@ -317,25 +345,43 @@ static void dumpObject(const ObjectFile *Obj) { if (opts::MipsReginfo) Dumper->printMipsReginfo(); } - if (opts::COFFImports) - Dumper->printCOFFImports(); - if (opts::COFFExports) - Dumper->printCOFFExports(); - if (opts::COFFDirectives) - Dumper->printCOFFDirectives(); - if (opts::COFFBaseRelocs) - Dumper->printCOFFBaseReloc(); - + if (Obj->isCOFF()) { + if (opts::COFFImports) + Dumper->printCOFFImports(); + if (opts::COFFExports) + Dumper->printCOFFExports(); + if (opts::COFFDirectives) + Dumper->printCOFFDirectives(); + if (opts::COFFBaseRelocs) + Dumper->printCOFFBaseReloc(); + if (opts::CodeView) + Dumper->printCodeViewDebugInfo(); + } + if (Obj->isMachO()) { + if (opts::MachODataInCode) + Dumper->printMachODataInCode(); + if (opts::MachOIndirectSymbols) + Dumper->printMachOIndirectSymbols(); + if (opts::MachOLinkerOptions) + Dumper->printMachOLinkerOptions(); + if (opts::MachOSegment) + Dumper->printMachOSegment(); + if (opts::MachOVersionMin) + Dumper->printMachOVersionMin(); + if (opts::MachODysymtab) + Dumper->printMachODysymtab(); + } if (opts::PrintStackMap) Dumper->printStackMap(); } /// @brief Dumps each object file in \a Arc; static void dumpArchive(const Archive *Arc) { - for (Archive::child_iterator ArcI = Arc->child_begin(), - ArcE = Arc->child_end(); - ArcI != ArcE; ++ArcI) { - ErrorOr<std::unique_ptr<Binary>> ChildOrErr = ArcI->getAsBinary(); + for (auto &ErrorOrChild : Arc->children()) { + if (std::error_code EC = ErrorOrChild.getError()) + reportError(Arc->getFileName(), EC.message()); + const auto &Child = *ErrorOrChild; + ErrorOr<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary(); if (std::error_code EC = ChildOrErr.getError()) { // Ignore non-object files. if (EC != object_error::invalid_file_type) @@ -365,18 +411,11 @@ static void dumpMachOUniversalBinary(const MachOUniversalBinary *UBinary) { /// @brief Opens \a File and dumps it. static void dumpInput(StringRef File) { - // If file isn't stdin, check that it exists. - if (File != "-" && !sys::fs::exists(File)) { - reportError(File, readobj_error::file_not_found); - return; - } // Attempt to open the binary. ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(File); - if (std::error_code EC = BinaryOrErr.getError()) { + if (std::error_code EC = BinaryOrErr.getError()) reportError(File, EC); - return; - } Binary &Binary = *BinaryOrErr.get().getBinary(); if (Archive *Arc = dyn_cast<Archive>(&Binary)) @@ -386,6 +425,8 @@ static void dumpInput(StringRef File) { dumpMachOUniversalBinary(UBinary); else if (ObjectFile *Obj = dyn_cast<ObjectFile>(&Binary)) dumpObject(Obj); + else if (COFFImportFile *Import = dyn_cast<COFFImportFile>(&Binary)) + dumpCOFFImportFile(Import); else reportError(File, readobj_error::unrecognized_file_format); } @@ -407,5 +448,5 @@ int main(int argc, const char *argv[]) { std::for_each(opts::InputFilenames.begin(), opts::InputFilenames.end(), dumpInput); - return ReturnValue; + return 0; } diff --git a/tools/llvm-readobj/llvm-readobj.h b/tools/llvm-readobj/llvm-readobj.h index 74b9a60d34b2..5a103920c165 100644 --- a/tools/llvm-readobj/llvm-readobj.h +++ b/tools/llvm-readobj/llvm-readobj.h @@ -11,6 +11,7 @@ #define LLVM_TOOLS_LLVM_READOBJ_LLVM_READOBJ_H #include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compiler.h" #include <string> namespace llvm { @@ -19,7 +20,8 @@ namespace llvm { } // Various helper functions. - bool error(std::error_code ec); + LLVM_ATTRIBUTE_NORETURN void reportError(Twine Msg); + void error(std::error_code ec); bool relocAddressLess(object::RelocationRef A, object::RelocationRef B); } // namespace llvm diff --git a/tools/llvm-rtdyld/llvm-rtdyld.cpp b/tools/llvm-rtdyld/llvm-rtdyld.cpp index 86f66f89b159..6ee3a44b63bf 100644 --- a/tools/llvm-rtdyld/llvm-rtdyld.cpp +++ b/tools/llvm-rtdyld/llvm-rtdyld.cpp @@ -94,6 +94,11 @@ CheckFiles("check", cl::ZeroOrMore); static cl::opt<uint64_t> +PreallocMemory("preallocate", + cl::desc("Allocate memory upfront rather than on-demand"), + cl::init(0)); + +static cl::opt<uint64_t> TargetAddrStart("target-addr-start", cl::desc("For -verify only: start of phony target address " "range."), @@ -127,6 +132,12 @@ DummySymbolMappings("dummy-extern", cl::ZeroOrMore, cl::Hidden); +static cl::opt<bool> +PrintAllocationRequests("print-alloc-requests", + cl::desc("Print allocation requests made to the memory " + "manager by RuntimeDyld"), + cl::Hidden); + /* *** */ // A trivial memory manager that doesn't do anything fancy, just uses the @@ -150,12 +161,6 @@ public: bool finalizeMemory(std::string *ErrMsg) override { return false; } - // Invalidate instruction cache for sections with execute permissions. - // Some platforms with separate data cache and instruction cache require - // explicit cache flush, otherwise JIT code manipulations (like resolved - // relocations) will get to the data cache but not to the instruction cache. - virtual void invalidateInstructionCache(); - void addDummySymbol(const std::string &Name, uint64_t Addr) { DummyExterns[Name] = Addr; } @@ -173,15 +178,56 @@ public: size_t Size) override {} void deregisterEHFrames(uint8_t *Addr, uint64_t LoadAddr, size_t Size) override {} + + void preallocateSlab(uint64_t Size) { + std::string Err; + sys::MemoryBlock MB = sys::Memory::AllocateRWX(Size, nullptr, &Err); + if (!MB.base()) + report_fatal_error("Can't allocate enough memory: " + Err); + + PreallocSlab = MB; + UsePreallocation = true; + SlabSize = Size; + } + + uint8_t *allocateFromSlab(uintptr_t Size, unsigned Alignment, bool isCode) { + Size = RoundUpToAlignment(Size, Alignment); + if (CurrentSlabOffset + Size > SlabSize) + report_fatal_error("Can't allocate enough memory. Tune --preallocate"); + + uintptr_t OldSlabOffset = CurrentSlabOffset; + sys::MemoryBlock MB((void *)OldSlabOffset, Size); + if (isCode) + FunctionMemory.push_back(MB); + else + DataMemory.push_back(MB); + CurrentSlabOffset += Size; + return (uint8_t*)OldSlabOffset; + } + private: std::map<std::string, uint64_t> DummyExterns; + sys::MemoryBlock PreallocSlab; + bool UsePreallocation = false; + uintptr_t SlabSize = 0; + uintptr_t CurrentSlabOffset = 0; }; uint8_t *TrivialMemoryManager::allocateCodeSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, StringRef SectionName) { - sys::MemoryBlock MB = sys::Memory::AllocateRWX(Size, nullptr, nullptr); + if (PrintAllocationRequests) + outs() << "allocateCodeSection(Size = " << Size << ", Alignment = " + << Alignment << ", SectionName = " << SectionName << ")\n"; + + if (UsePreallocation) + return allocateFromSlab(Size, Alignment, true /* isCode */); + + std::string Err; + sys::MemoryBlock MB = sys::Memory::AllocateRWX(Size, nullptr, &Err); + if (!MB.base()) + report_fatal_error("MemoryManager allocation failed: " + Err); FunctionMemory.push_back(MB); return (uint8_t*)MB.base(); } @@ -191,41 +237,35 @@ uint8_t *TrivialMemoryManager::allocateDataSection(uintptr_t Size, unsigned SectionID, StringRef SectionName, bool IsReadOnly) { - sys::MemoryBlock MB = sys::Memory::AllocateRWX(Size, nullptr, nullptr); - DataMemory.push_back(MB); - return (uint8_t*)MB.base(); -} + if (PrintAllocationRequests) + outs() << "allocateDataSection(Size = " << Size << ", Alignment = " + << Alignment << ", SectionName = " << SectionName << ")\n"; -void TrivialMemoryManager::invalidateInstructionCache() { - for (int i = 0, e = FunctionMemory.size(); i != e; ++i) - sys::Memory::InvalidateInstructionCache(FunctionMemory[i].base(), - FunctionMemory[i].size()); + if (UsePreallocation) + return allocateFromSlab(Size, Alignment, false /* isCode */); - for (int i = 0, e = DataMemory.size(); i != e; ++i) - sys::Memory::InvalidateInstructionCache(DataMemory[i].base(), - DataMemory[i].size()); + std::string Err; + sys::MemoryBlock MB = sys::Memory::AllocateRWX(Size, nullptr, &Err); + if (!MB.base()) + report_fatal_error("MemoryManager allocation failed: " + Err); + DataMemory.push_back(MB); + return (uint8_t*)MB.base(); } static const char *ProgramName; -static void Message(const char *Type, const Twine &Msg) { - errs() << ProgramName << ": " << Type << ": " << Msg << "\n"; -} - static int Error(const Twine &Msg) { - Message("error", Msg); + errs() << ProgramName << ": error: " << Msg << "\n"; return 1; } static void loadDylibs() { for (const std::string &Dylib : Dylibs) { - if (sys::fs::is_regular_file(Dylib)) { - std::string ErrMsg; - if (sys::DynamicLibrary::LoadLibraryPermanently(Dylib.c_str(), &ErrMsg)) - llvm::errs() << "Error loading '" << Dylib << "': " - << ErrMsg << "\n"; - } else - llvm::errs() << "Dylib not found: '" << Dylib << "'.\n"; + if (!sys::fs::is_regular_file(Dylib)) + report_fatal_error("Dylib not found: '" + Dylib + "'."); + std::string ErrMsg; + if (sys::DynamicLibrary::LoadLibraryPermanently(Dylib.c_str(), &ErrMsg)) + report_fatal_error("Error loading '" + Dylib + "': " + ErrMsg); } } @@ -240,7 +280,7 @@ static int printLineInfoForInput(bool LoadObjects, bool UseDebugObj) { // If we don't have any input files, read from stdin. if (!InputFileList.size()) InputFileList.push_back("-"); - for(unsigned i = 0, e = InputFileList.size(); i != e; ++i) { + for (auto &File : InputFileList) { // Instantiate a dynamic linker. TrivialMemoryManager MemMgr; RuntimeDyld Dyld(MemMgr, MemMgr); @@ -248,7 +288,7 @@ static int printLineInfoForInput(bool LoadObjects, bool UseDebugObj) { // Load the input memory buffer. ErrorOr<std::unique_ptr<MemoryBuffer>> InputBuffer = - MemoryBuffer::getFileOrSTDIN(InputFileList[i]); + MemoryBuffer::getFileOrSTDIN(File); if (std::error_code EC = InputBuffer.getError()) return Error("unable to read input: '" + EC.message() + "'"); @@ -277,6 +317,7 @@ static int printLineInfoForInput(bool LoadObjects, bool UseDebugObj) { if (UseDebugObj) { DebugObj = LoadedObjInfo->getObjectForDebug(Obj); SymbolObj = DebugObj.getBinary(); + LoadedObjInfo.reset(); } } @@ -303,12 +344,11 @@ static int printLineInfoForInput(bool LoadObjects, bool UseDebugObj) { // symbol in memory (rather than that in the unrelocated object file) // and use that to query the DWARFContext. if (!UseDebugObj && LoadObjects) { - object::section_iterator Sec(SymbolObj->section_end()); - Sym.getSection(Sec); + object::section_iterator Sec = *Sym.getSection(); StringRef SecName; Sec->getName(SecName); uint64_t SectionLoadAddress = - LoadedObjInfo->getSectionLoadAddress(SecName); + LoadedObjInfo->getSectionLoadAddress(*Sec); if (SectionLoadAddress != 0) Addr += SectionLoadAddress - Sec->getAddress(); } @@ -317,11 +357,9 @@ static int printLineInfoForInput(bool LoadObjects, bool UseDebugObj) { << ", Addr = " << Addr << "\n"; DILineInfoTable Lines = Context->getLineInfoForAddressRange(Addr, Size); - DILineInfoTable::iterator Begin = Lines.begin(); - DILineInfoTable::iterator End = Lines.end(); - for (DILineInfoTable::iterator It = Begin; It != End; ++It) { - outs() << " Line info @ " << It->first - Addr << ": " - << It->second.FileName << ", line:" << It->second.Line << "\n"; + for (auto &D : Lines) { + outs() << " Line info @ " << D.first - Addr << ": " + << D.second.FileName << ", line:" << D.second.Line << "\n"; } } } @@ -330,26 +368,33 @@ static int printLineInfoForInput(bool LoadObjects, bool UseDebugObj) { return 0; } +static void doPreallocation(TrivialMemoryManager &MemMgr) { + // Allocate a slab of memory upfront, if required. This is used if + // we want to test small code models. + if (static_cast<intptr_t>(PreallocMemory) < 0) + report_fatal_error("Pre-allocated bytes of memory must be a positive integer."); + + // FIXME: Limit the amount of memory that can be preallocated? + if (PreallocMemory != 0) + MemMgr.preallocateSlab(PreallocMemory); +} + static int executeInput() { // Load any dylibs requested on the command line. loadDylibs(); // Instantiate a dynamic linker. TrivialMemoryManager MemMgr; + doPreallocation(MemMgr); RuntimeDyld Dyld(MemMgr, MemMgr); - // FIXME: Preserve buffers until resolveRelocations time to work around a bug - // in RuntimeDyldELF. - // This fixme should be fixed ASAP. This is a very brittle workaround. - std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers; - // If we don't have any input files, read from stdin. if (!InputFileList.size()) InputFileList.push_back("-"); - for(unsigned i = 0, e = InputFileList.size(); i != e; ++i) { + for (auto &File : InputFileList) { // Load the input memory buffer. ErrorOr<std::unique_ptr<MemoryBuffer>> InputBuffer = - MemoryBuffer::getFileOrSTDIN(InputFileList[i]); + MemoryBuffer::getFileOrSTDIN(File); if (std::error_code EC = InputBuffer.getError()) return Error("unable to read input: '" + EC.message() + "'"); ErrorOr<std::unique_ptr<ObjectFile>> MaybeObj( @@ -359,7 +404,6 @@ static int executeInput() { return Error("unable to create object file: '" + EC.message() + "'"); ObjectFile &Obj = **MaybeObj; - InputBuffers.push_back(std::move(*InputBuffer)); // Load the object file Dyld.loadObject(Obj); @@ -368,12 +412,9 @@ static int executeInput() { } } - // Resolve all the relocations we can. - Dyld.resolveRelocations(); - // Clear instruction cache before code will be executed. - MemMgr.invalidateInstructionCache(); - + // Resove all the relocations we can. // FIXME: Error out if there are unresolved relocations. + Dyld.resolveRelocations(); // Get the address of the entry point (_main by default). void *MainAddress = Dyld.getSymbolLocalAddress(EntryPoint); @@ -381,12 +422,12 @@ static int executeInput() { return Error("no definition for '" + EntryPoint + "'"); // Invalidate the instruction cache for each loaded function. - for (unsigned i = 0, e = MemMgr.FunctionMemory.size(); i != e; ++i) { - sys::MemoryBlock &Data = MemMgr.FunctionMemory[i]; + for (auto &FM : MemMgr.FunctionMemory) { + // Make sure the memory is executable. + // setExecutable will call InvalidateInstructionCache. std::string ErrorStr; - sys::Memory::InvalidateInstructionCache(Data.base(), Data.size()); - if (!sys::Memory::setExecutable(Data, &ErrorStr)) + if (!sys::Memory::setExecutable(FM, &ErrorStr)) return Error("unable to mark function executable: '" + ErrorStr + "'"); } @@ -428,11 +469,9 @@ applySpecificSectionMappings(RuntimeDyldChecker &Checker) { std::string 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); - } + if (ComaIdx == StringRef::npos) + report_fatal_error("Invalid section specification '" + Mapping + + "'. Should be '<file name>,<section name>=<addr>'"); std::string FileName = SectionIDStr.substr(0, ComaIdx); std::string SectionName = SectionIDStr.substr(ComaIdx + 1); @@ -442,20 +481,17 @@ applySpecificSectionMappings(RuntimeDyldChecker &Checker) { std::tie(OldAddrInt, ErrorMsg) = Checker.getSectionAddr(FileName, SectionName, true); - if (ErrorMsg != "") { - errs() << ErrorMsg; - exit(1); - } + if (ErrorMsg != "") + report_fatal_error(ErrorMsg); void* OldAddr = reinterpret_cast<void*>(static_cast<uintptr_t>(OldAddrInt)); std::string NewAddrStr = Mapping.substr(EqualsIdx + 1); uint64_t NewAddr; - if (StringRef(NewAddrStr).getAsInteger(0, NewAddr)) { - errs() << "Invalid section address in mapping '" << Mapping << "'.\n"; - exit(1); - } + if (StringRef(NewAddrStr).getAsInteger(0, NewAddr)) + report_fatal_error("Invalid section address in mapping '" + Mapping + + "'."); Checker.getRTDyld().mapSectionAddress(OldAddr, NewAddr); SpecificMappings[OldAddr] = NewAddr; @@ -544,20 +580,16 @@ static void remapSectionsAndSymbols(const llvm::Triple &TargetTriple, for (const auto &Mapping : DummySymbolMappings) { size_t EqualsIdx = Mapping.find_first_of("="); - if (EqualsIdx == StringRef::npos) { - errs() << "Invalid dummy symbol specification '" << Mapping - << "'. Should be '<symbol name>=<addr>'\n"; - exit(1); - } + if (EqualsIdx == StringRef::npos) + report_fatal_error("Invalid dummy symbol specification '" + Mapping + + "'. Should be '<symbol name>=<addr>'"); std::string Symbol = Mapping.substr(0, EqualsIdx); std::string AddrStr = Mapping.substr(EqualsIdx + 1); uint64_t Addr; - if (StringRef(AddrStr).getAsInteger(0, Addr)) { - errs() << "Invalid symbol mapping '" << Mapping << "'.\n"; - exit(1); - } + if (StringRef(AddrStr).getAsInteger(0, Addr)) + report_fatal_error("Invalid symbol mapping '" + Mapping + "'."); MemMgr.addDummySymbol(Symbol, Addr); } @@ -569,38 +601,38 @@ static void remapSectionsAndSymbols(const llvm::Triple &TargetTriple, static int linkAndVerify() { // Check for missing triple. - if (TripleName == "") { - llvm::errs() << "Error: -triple required when running in -verify mode.\n"; - return 1; - } + if (TripleName == "") + return Error("-triple required when running in -verify mode."); // Look up the target and build the disassembler. Triple TheTriple(Triple::normalize(TripleName)); std::string ErrorStr; const Target *TheTarget = TargetRegistry::lookupTarget("", TheTriple, ErrorStr); - if (!TheTarget) { - llvm::errs() << "Error accessing target '" << TripleName << "': " - << ErrorStr << "\n"; - return 1; - } + if (!TheTarget) + return Error("Error accessing target '" + TripleName + "': " + ErrorStr); + TripleName = TheTriple.getTriple(); std::unique_ptr<MCSubtargetInfo> STI( TheTarget->createMCSubtargetInfo(TripleName, MCPU, "")); - assert(STI && "Unable to create subtarget info!"); + if (!STI) + return Error("Unable to create subtarget info!"); std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName)); - assert(MRI && "Unable to create target register info!"); + if (!MRI) + return Error("Unable to create target register info!"); std::unique_ptr<MCAsmInfo> MAI(TheTarget->createMCAsmInfo(*MRI, TripleName)); - assert(MAI && "Unable to create target asm info!"); + if (!MAI) + return Error("Unable to create target asm info!"); MCContext Ctx(MAI.get(), MRI.get(), nullptr); std::unique_ptr<MCDisassembler> Disassembler( TheTarget->createMCDisassembler(*STI, Ctx)); - assert(Disassembler && "Unable to create disassembler!"); + if (!Disassembler) + return Error("Unable to create disassembler!"); std::unique_ptr<MCInstrInfo> MII(TheTarget->createMCInstrInfo()); @@ -612,23 +644,19 @@ static int linkAndVerify() { // Instantiate a dynamic linker. TrivialMemoryManager MemMgr; + doPreallocation(MemMgr); RuntimeDyld Dyld(MemMgr, MemMgr); Dyld.setProcessAllSections(true); RuntimeDyldChecker Checker(Dyld, Disassembler.get(), InstPrinter.get(), llvm::dbgs()); - // FIXME: Preserve buffers until resolveRelocations time to work around a bug - // in RuntimeDyldELF. - // This fixme should be fixed ASAP. This is a very brittle workaround. - std::vector<std::unique_ptr<MemoryBuffer>> InputBuffers; - // If we don't have any input files, read from stdin. if (!InputFileList.size()) InputFileList.push_back("-"); - for(unsigned i = 0, e = InputFileList.size(); i != e; ++i) { + for (auto &Filename : InputFileList) { // Load the input memory buffer. ErrorOr<std::unique_ptr<MemoryBuffer>> InputBuffer = - MemoryBuffer::getFileOrSTDIN(InputFileList[i]); + MemoryBuffer::getFileOrSTDIN(Filename); if (std::error_code EC = InputBuffer.getError()) return Error("unable to read input: '" + EC.message() + "'"); @@ -640,7 +668,6 @@ static int linkAndVerify() { return Error("unable to create object file: '" + EC.message() + "'"); ObjectFile &Obj = **MaybeObj; - InputBuffers.push_back(std::move(*InputBuffer)); // Load the object file Dyld.loadObject(Obj); @@ -660,11 +687,9 @@ static int linkAndVerify() { Dyld.registerEHFrames(); int ErrorCode = checkAllExpressions(Checker); - if (Dyld.hasError()) { - errs() << "RTDyld reported an error applying relocations:\n " - << Dyld.getErrorString() << "\n"; - ErrorCode = 1; - } + if (Dyld.hasError()) + return Error("RTDyld reported an error applying relocations:\n " + + Dyld.getErrorString()); return ErrorCode; } diff --git a/tools/llvm-shlib/CMakeLists.txt b/tools/llvm-shlib/CMakeLists.txt index 54d71d3f6320..2356103a9cd5 100644 --- a/tools/llvm-shlib/CMakeLists.txt +++ b/tools/llvm-shlib/CMakeLists.txt @@ -2,42 +2,6 @@ # 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 - DebugInfoDWARF - DebugInfoPDB - 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 @@ -46,56 +10,35 @@ set(SOURCES llvm_map_components_to_libnames(LIB_NAMES ${LLVM_DYLIB_COMPONENTS}) -if(NOT DEFINED LLVM_DYLIB_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.") +if(LLVM_LINK_LLVM_DYLIB) + if(LLVM_DYLIB_EXPORTED_SYMBOL_FILE) + message(WARNING "Using LLVM_LINK_LLVM_DYLIB with LLVM_DYLIB_EXPORTED_SYMBOL_FILE may not work. Use at your own risk.") 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) - - if (NOT LLVM_DYLIB_EXPORT_ALL) - foreach (lib ${LIB_NAMES}) - set(LIB_DIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX}) - 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 () - endif() - - if (LLVM_DYLIB_EXPORT_ALL) - add_custom_command(OUTPUT ${LLVM_EXPORTED_SYMBOL_FILE} - COMMAND echo \"LLVM*\" > ${LLVM_EXPORTED_SYMBOL_FILE} && echo \"_Z*llvm*\" >> ${LLVM_EXPORTED_SYMBOL_FILE} - WORKING_DIRECTORY ${LIB_DIR} - DEPENDS ${LLVM_DYLIB_REQUIRED_EXPORTS} - COMMENT "Generating combined export list...") - else() - 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() + # libLLVM.so should not have any dependencies on any other LLVM + # shared libraries. When using the "all" pseudo-component, + # LLVM_AVAILABLE_LIBS is added to the dependencies, which may + # contain shared libraries (e.g. libLTO). + # + # Also exclude libLLVMTableGen for the following reasons: + # - it is only used by internal *-tblgen utilities; + # - it pollutes the global options space. + foreach(lib ${LIB_NAMES}) + get_target_property(t ${lib} TYPE) + if("${lib}" STREQUAL "LLVMTableGen") + elseif("x${t}" STREQUAL "xSTATIC_LIBRARY") + list(APPEND FILTERED_LIB_NAMES ${lib}) + endif() + endforeach() + set(LIB_NAMES ${FILTERED_LIB_NAMES}) +endif() - add_custom_target(libLLVMExports DEPENDS ${LLVM_EXPORTED_SYMBOL_FILE}) -else() +if(LLVM_DYLIB_EXPORTED_SYMBOL_FILE) set(LLVM_EXPORTED_SYMBOL_FILE ${LLVM_DYLIB_EXPORTED_SYMBOL_FILE}) add_custom_target(libLLVMExports DEPENDS ${LLVM_EXPORTED_SYMBOL_FILE}) endif() -add_llvm_library(LLVM SHARED ${SOURCES}) +add_llvm_library(LLVM SHARED DISABLE_LLVM_LINK_LLVM_DYLIB SONAME ${SOURCES}) list(REMOVE_DUPLICATES LIB_NAMES) if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") # FIXME: It should be "GNU ld for elf" @@ -107,11 +50,48 @@ endif() target_link_libraries(LLVM PRIVATE ${LIB_NAMES}) -add_dependencies(LLVM libLLVMExports) - 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}") + " -compatibility_version 1 -current_version ${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}") +endif() + +if(TARGET libLLVMExports) + add_dependencies(LLVM libLLVMExports) +endif() + +if(LLVM_BUILD_LLVM_C_DYLIB) + # 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 + + if(NOT APPLE) + message(FATAL_ERROR "Generating libLLVM-c is only supported on Darwin") + endif() + + set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_BINARY_DIR}/libllvm-c.exports) + + set(LIB_DIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX}) + set(LIB_NAME ${LIB_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}LLVM) + set(LIB_PATH ${LIB_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX}) + set(LIB_EXPORTS_PATH ${LIB_NAME}.exports) + list(APPEND LLVM_DYLIB_REQUIRED_EXPORTS ${LIB_EXPORTS_PATH}) + + add_custom_command(OUTPUT ${LLVM_EXPORTED_SYMBOL_FILE} + COMMAND nm ${LIB_PATH} | awk "/T _LLVM/ || /T LLVM/ { print $3 }" | sort -u | sed -e "s/^_//g" > ${LLVM_EXPORTED_SYMBOL_FILE} + WORKING_DIRECTORY ${LIB_DIR} + DEPENDS LLVM + COMMENT "Generating Export list for LLVM..." + VERBATIM ) + + add_custom_target(libLLVMCExports DEPENDS ${LLVM_EXPORTED_SYMBOL_FILE}) + + add_llvm_library(LLVM-C SHARED ${SOURCES}) + + target_link_libraries(LLVM-C PUBLIC LLVM) + add_dependencies(LLVM-C libLLVMCExports) + + set_property(TARGET LLVM-C APPEND_STRING PROPERTY + LINK_FLAGS + " -compatibility_version 1 -current_version ${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH} -Wl,-reexport_library ${LIB_PATH}") endif() diff --git a/tools/llvm-shlib/Makefile b/tools/llvm-shlib/Makefile index 2bc81dac5f00..19077a3858a6 100644 --- a/tools/llvm-shlib/Makefile +++ b/tools/llvm-shlib/Makefile @@ -61,7 +61,7 @@ endif ifeq ($(HOST_OS), $(filter $(HOST_OS), DragonFly Linux FreeBSD GNU/kFreeBSD GNU)) # Add soname to the library. - LLVMLibsOptions += -Wl,--soname,lib$(LIBRARYNAME).1$(SHLIBEXT) + LLVMLibsOptions += -Wl,--soname,lib$(LIBRARYNAME)$(SHLIBEXT) endif ifeq ($(HOST_OS), $(filter $(HOST_OS), Linux GNU GNU/kFreeBSD)) diff --git a/tools/llvm-size/llvm-size.cpp b/tools/llvm-size/llvm-size.cpp index 9a6e2c1ae4be..069cc621f615 100644 --- a/tools/llvm-size/llvm-size.cpp +++ b/tools/llvm-size/llvm-size.cpp @@ -1,4 +1,4 @@ -//===-- llvm-size.cpp - Print the size of each object section -------------===// +//===-- llvm-size.cpp - Print the size of each object section ---*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -30,6 +30,7 @@ #include <algorithm> #include <string> #include <system_error> + using namespace llvm; using namespace object; @@ -98,7 +99,7 @@ static size_t getNumLengthAsString(uint64_t num) { } /// @brief Return the printing format for the Radix. -static const char *getRadixFmt(void) { +static const char *getRadixFmt() { switch (Radix) { case octal: return PRIo64; @@ -413,14 +414,6 @@ static bool checkMachOAndArchFlags(ObjectFile *o, StringRef file) { /// @brief Print the section sizes for @p file. If @p file is an archive, print /// the section sizes for each archive member. static void PrintFileSectionSizes(StringRef file) { - // If file is not stdin, check that it exists. - if (file != "-") { - if (!sys::fs::exists(file)) { - errs() << ToolName << ": '" << file << "': " - << "No such file\n"; - return; - } - } // Attempt to open the binary. ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(file); @@ -435,7 +428,13 @@ static void PrintFileSectionSizes(StringRef file) { for (object::Archive::child_iterator i = a->child_begin(), e = a->child_end(); i != e; ++i) { - ErrorOr<std::unique_ptr<Binary>> ChildOrErr = i->getAsBinary(); + if (i->getError()) { + errs() << ToolName << ": " << file << ": " << i->getError().message() + << ".\n"; + exit(1); + } + auto &c = i->get(); + ErrorOr<std::unique_ptr<Binary>> ChildOrErr = c.getAsBinary(); if (std::error_code EC = ChildOrErr.getError()) { errs() << ToolName << ": " << file << ": " << EC.message() << ".\n"; continue; @@ -497,7 +496,13 @@ static void PrintFileSectionSizes(StringRef file) { for (object::Archive::child_iterator i = UA->child_begin(), e = UA->child_end(); i != e; ++i) { - ErrorOr<std::unique_ptr<Binary>> ChildOrErr = i->getAsBinary(); + if (std::error_code EC = i->getError()) { + errs() << ToolName << ": " << file << ": " << EC.message() + << ".\n"; + exit(1); + } + auto &c = i->get(); + ErrorOr<std::unique_ptr<Binary>> ChildOrErr = c.getAsBinary(); if (std::error_code EC = ChildOrErr.getError()) { errs() << ToolName << ": " << file << ": " << EC.message() << ".\n"; @@ -574,7 +579,13 @@ static void PrintFileSectionSizes(StringRef file) { for (object::Archive::child_iterator i = UA->child_begin(), e = UA->child_end(); i != e; ++i) { - ErrorOr<std::unique_ptr<Binary>> ChildOrErr = i->getAsBinary(); + if (std::error_code EC = i->getError()) { + errs() << ToolName << ": " << file << ": " << EC.message() + << ".\n"; + exit(1); + } + auto &c = i->get(); + ErrorOr<std::unique_ptr<Binary>> ChildOrErr = c.getAsBinary(); if (std::error_code EC = ChildOrErr.getError()) { errs() << ToolName << ": " << file << ": " << EC.message() << ".\n"; @@ -638,7 +649,12 @@ static void PrintFileSectionSizes(StringRef file) { for (object::Archive::child_iterator i = UA->child_begin(), e = UA->child_end(); i != e; ++i) { - ErrorOr<std::unique_ptr<Binary>> ChildOrErr = i->getAsBinary(); + if (std::error_code EC = i->getError()) { + errs() << ToolName << ": " << file << ": " << EC.message() << ".\n"; + exit(1); + } + auto &c = i->get(); + ErrorOr<std::unique_ptr<Binary>> ChildOrErr = c.getAsBinary(); if (std::error_code EC = ChildOrErr.getError()) { errs() << ToolName << ": " << file << ": " << EC.message() << ".\n"; continue; diff --git a/tools/llvm-split/CMakeLists.txt b/tools/llvm-split/CMakeLists.txt new file mode 100644 index 000000000000..6e22a7236714 --- /dev/null +++ b/tools/llvm-split/CMakeLists.txt @@ -0,0 +1,11 @@ +set(LLVM_LINK_COMPONENTS + TransformUtils + BitWriter + Core + IRReader + Support + ) + +add_llvm_tool(llvm-split + llvm-split.cpp + ) diff --git a/tools/macho-dump/LLVMBuild.txt b/tools/llvm-split/LLVMBuild.txt index 1ad9b84261c9..780c76afa41c 100644 --- a/tools/macho-dump/LLVMBuild.txt +++ b/tools/llvm-split/LLVMBuild.txt @@ -1,4 +1,4 @@ -;===- ./tools/macho-dump/LLVMBuild.txt -------------------------*- Conf -*--===; +;===- ./tools/llvm-split/LLVMBuild.txt -------------------------*- Conf -*--===; ; ; The LLVM Compiler Infrastructure ; @@ -17,6 +17,6 @@ [component_0] type = Tool -name = macho-dump +name = llvm-split parent = Tools -required_libraries = Object Support +required_libraries = TransformUtils BitWriter Core IRReader Support diff --git a/tools/macho-dump/Makefile b/tools/llvm-split/Makefile index 0843e982e1af..ef1243dbf1ff 100644 --- a/tools/macho-dump/Makefile +++ b/tools/llvm-split/Makefile @@ -1,15 +1,15 @@ -##===- tools/macho-dump/Makefile ---------------------------*- Makefile -*-===## -# +##===- tools/llvm-split/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 := macho-dump -LINK_COMPONENTS := support object +TOOLNAME := llvm-split +LINK_COMPONENTS := transformutils bitwriter core irreader support # This tool has no plugins, optimize startup time. TOOL_NO_EXPORTS := 1 diff --git a/tools/llvm-split/llvm-split.cpp b/tools/llvm-split/llvm-split.cpp new file mode 100644 index 000000000000..059770fbf4ac --- /dev/null +++ b/tools/llvm-split/llvm-split.cpp @@ -0,0 +1,67 @@ +//===-- llvm-split: command line tool for testing module splitter ---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This program can be used to test the llvm::SplitModule function. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/StringExtras.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IRReader/IRReader.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/Utils/SplitModule.h" + +using namespace llvm; + +static cl::opt<std::string> +InputFilename(cl::Positional, cl::desc("<input bitcode file>"), + cl::init("-"), cl::value_desc("filename")); + +static cl::opt<std::string> +OutputFilename("o", cl::desc("Override output filename"), + cl::value_desc("filename")); + +static cl::opt<unsigned> NumOutputs("j", cl::Prefix, cl::init(2), + cl::desc("Number of output files")); + +int main(int argc, char **argv) { + LLVMContext &Context = getGlobalContext(); + SMDiagnostic Err; + cl::ParseCommandLineOptions(argc, argv, "LLVM module splitter\n"); + + std::unique_ptr<Module> M = parseIRFile(InputFilename, Err, Context); + + if (!M) { + Err.print(argv[0], errs()); + return 1; + } + + unsigned I = 0; + SplitModule(std::move(M), NumOutputs, [&](std::unique_ptr<Module> MPart) { + std::error_code EC; + std::unique_ptr<tool_output_file> Out(new tool_output_file( + OutputFilename + utostr(I++), EC, sys::fs::F_None)); + if (EC) { + errs() << EC.message() << '\n'; + exit(1); + } + + WriteBitcodeToFile(MPart.get(), Out->os()); + + // Declare success. + Out->keep(); + }); + + return 0; +} diff --git a/tools/llvm-stress/CMakeLists.txt b/tools/llvm-stress/CMakeLists.txt index d5c10e13f5b3..8f2b30e4163b 100644 --- a/tools/llvm-stress/CMakeLists.txt +++ b/tools/llvm-stress/CMakeLists.txt @@ -1,6 +1,6 @@ set(LLVM_LINK_COMPONENTS + Analysis Core - IPA Support ) diff --git a/tools/llvm-stress/llvm-stress.cpp b/tools/llvm-stress/llvm-stress.cpp index 6a1a248a0572..99d2afdcd301 100644 --- a/tools/llvm-stress/llvm-stress.cpp +++ b/tools/llvm-stress/llvm-stress.cpp @@ -612,7 +612,8 @@ struct CmpModifier: public Modifier { } Value *V = CmpInst::Create(fp ? Instruction::FCmp : Instruction::ICmp, - op, Val0, Val1, "Cmp", BB->getTerminator()); + (CmpInst::Predicate)op, Val0, Val1, "Cmp", + BB->getTerminator()); return PT->push_back(V); } }; @@ -666,7 +667,7 @@ static void IntroduceControlFlow(Function *F, Random &R) { for (auto *Instr : BoolInst) { BasicBlock *Curr = Instr->getParent(); - BasicBlock::iterator Loc = Instr; + BasicBlock::iterator Loc = Instr->getIterator(); BasicBlock *Next = Curr->splitBasicBlock(Loc, "CF"); Instr->moveBefore(Curr->getTerminator()); if (Curr != &F->getEntryBlock()) { diff --git a/tools/llvm-symbolizer/CMakeLists.txt b/tools/llvm-symbolizer/CMakeLists.txt index 5df3b17a065e..b04c45ff7442 100644 --- a/tools/llvm-symbolizer/CMakeLists.txt +++ b/tools/llvm-symbolizer/CMakeLists.txt @@ -8,9 +8,9 @@ set(LLVM_LINK_COMPONENTS DebugInfoPDB Object Support + Symbolize ) add_llvm_tool(llvm-symbolizer - LLVMSymbolize.cpp llvm-symbolizer.cpp ) diff --git a/tools/llvm-symbolizer/LLVMSymbolize.cpp b/tools/llvm-symbolizer/LLVMSymbolize.cpp deleted file mode 100644 index c57c219b11d2..000000000000 --- a/tools/llvm-symbolizer/LLVMSymbolize.cpp +++ /dev/null @@ -1,532 +0,0 @@ -//===-- LLVMSymbolize.cpp -------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Implementation for LLVM symbolization library. -// -//===----------------------------------------------------------------------===// - -#include "LLVMSymbolize.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/Config/config.h" -#include "llvm/DebugInfo/DWARF/DWARFContext.h" -#include "llvm/DebugInfo/PDB/PDB.h" -#include "llvm/DebugInfo/PDB/PDBContext.h" -#include "llvm/Object/ELFObjectFile.h" -#include "llvm/Object/MachO.h" -#include "llvm/Object/SymbolSize.h" -#include "llvm/Support/Casting.h" -#include "llvm/Support/Compression.h" -#include "llvm/Support/DataExtractor.h" -#include "llvm/Support/Errc.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Path.h" -#include <sstream> -#include <stdlib.h> - -#if defined(_MSC_VER) -#include <Windows.h> -#include <DbgHelp.h> -#pragma comment(lib, "dbghelp.lib") -#endif - -namespace llvm { -namespace symbolize { - -static bool error(std::error_code ec) { - if (!ec) - return false; - errs() << "LLVMSymbolizer: error reading file: " << ec.message() << ".\n"; - return true; -} - -static DILineInfoSpecifier -getDILineInfoSpecifier(const LLVMSymbolizer::Options &Opts) { - return DILineInfoSpecifier( - DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, - Opts.PrintFunctions); -} - -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; - } - } - } - std::vector<std::pair<SymbolRef, uint64_t>> Symbols = - computeSymbolSizes(*Module); - for (auto &P : Symbols) - addSymbol(P.first, P.second, OpdExtractor.get(), OpdAddress); -} - -void ModuleInfo::addSymbol(const SymbolRef &Symbol, uint64_t SymbolSize, - DataExtractor *OpdExtractor, uint64_t OpdAddress) { - SymbolRef::Type SymbolType = Symbol.getType(); - if (SymbolType != SymbolRef::ST_Function && SymbolType != SymbolRef::ST_Data) - return; - ErrorOr<uint64_t> SymbolAddressOrErr = Symbol.getAddress(); - if (error(SymbolAddressOrErr.getError())) - return; - uint64_t SymbolAddress = *SymbolAddressOrErr; - 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); - } - ErrorOr<StringRef> SymbolNameOrErr = Symbol.getName(); - if (error(SymbolNameOrErr.getError())) - return; - StringRef SymbolName = *SymbolNameOrErr; - // Mach-O symbol table names have leading underscore, skip it. - if (Module->isMachO() && SymbolName.size() > 0 && SymbolName[0] == '_') - 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. - auto &M = SymbolType == SymbolRef::ST_Function ? Functions : Objects; - SymbolDesc SD = { SymbolAddress, SymbolSize }; - M.insert(std::make_pair(SD, SymbolName)); -} - -bool ModuleInfo::getNameFromSymbolTable(SymbolRef::Type Type, uint64_t Address, - std::string &Name, uint64_t &Addr, - uint64_t &Size) const { - const auto &SymbolMap = Type == SymbolRef::ST_Function ? Functions : Objects; - if (SymbolMap.empty()) - return false; - SymbolDesc SD = { Address, Address }; - auto SymbolIterator = SymbolMap.upper_bound(SD); - if (SymbolIterator == SymbolMap.begin()) - return false; - --SymbolIterator; - if (SymbolIterator->first.Size != 0 && - SymbolIterator->first.Addr + SymbolIterator->first.Size <= Address) - return false; - Name = SymbolIterator->second.str(); - Addr = SymbolIterator->first.Addr; - Size = SymbolIterator->first.Size; - return true; -} - -DILineInfo ModuleInfo::symbolizeCode( - uint64_t ModuleOffset, const LLVMSymbolizer::Options &Opts) const { - DILineInfo LineInfo; - if (DebugInfoContext) { - LineInfo = DebugInfoContext->getLineInfoForAddress( - ModuleOffset, getDILineInfoSpecifier(Opts)); - } - // Override function name from symbol table if necessary. - if (Opts.PrintFunctions != FunctionNameKind::None && Opts.UseSymbolTable) { - std::string FunctionName; - uint64_t Start, Size; - if (getNameFromSymbolTable(SymbolRef::ST_Function, ModuleOffset, - FunctionName, Start, Size)) { - LineInfo.FunctionName = FunctionName; - } - } - return LineInfo; -} - -DIInliningInfo ModuleInfo::symbolizeInlinedCode( - uint64_t ModuleOffset, const LLVMSymbolizer::Options &Opts) const { - DIInliningInfo InlinedContext; - - if (DebugInfoContext) { - InlinedContext = DebugInfoContext->getInliningInfoForAddress( - ModuleOffset, getDILineInfoSpecifier(Opts)); - } - // Make sure there is at least one frame in context. - if (InlinedContext.getNumberOfFrames() == 0) { - InlinedContext.addFrame(DILineInfo()); - } - // Override the function name in lower frame with name from symbol table. - if (Opts.PrintFunctions != FunctionNameKind::None && Opts.UseSymbolTable) { - DIInliningInfo PatchedInlinedContext; - for (uint32_t i = 0, n = InlinedContext.getNumberOfFrames(); i < n; i++) { - DILineInfo LineInfo = InlinedContext.getFrame(i); - if (i == n - 1) { - std::string FunctionName; - uint64_t Start, Size; - if (getNameFromSymbolTable(SymbolRef::ST_Function, ModuleOffset, - FunctionName, Start, Size)) { - LineInfo.FunctionName = FunctionName; - } - } - PatchedInlinedContext.addFrame(LineInfo); - } - InlinedContext = PatchedInlinedContext; - } - return InlinedContext; -} - -bool ModuleInfo::symbolizeData(uint64_t ModuleOffset, std::string &Name, - uint64_t &Start, uint64_t &Size) const { - return getNameFromSymbolTable(SymbolRef::ST_Data, ModuleOffset, Name, Start, - Size); -} - -const char LLVMSymbolizer::kBadString[] = "??"; - -std::string LLVMSymbolizer::symbolizeCode(const std::string &ModuleName, - uint64_t ModuleOffset) { - ModuleInfo *Info = getOrCreateModuleInfo(ModuleName); - if (!Info) - return printDILineInfo(DILineInfo()); - if (Opts.PrintInlining) { - DIInliningInfo InlinedContext = - Info->symbolizeInlinedCode(ModuleOffset, Opts); - uint32_t FramesNum = InlinedContext.getNumberOfFrames(); - assert(FramesNum > 0); - std::string Result; - for (uint32_t i = 0; i < FramesNum; i++) { - DILineInfo LineInfo = InlinedContext.getFrame(i); - Result += printDILineInfo(LineInfo); - } - return Result; - } - DILineInfo LineInfo = Info->symbolizeCode(ModuleOffset, Opts); - return printDILineInfo(LineInfo); -} - -std::string LLVMSymbolizer::symbolizeData(const std::string &ModuleName, - uint64_t ModuleOffset) { - std::string Name = kBadString; - uint64_t Start = 0; - uint64_t Size = 0; - if (Opts.UseSymbolTable) { - if (ModuleInfo *Info = getOrCreateModuleInfo(ModuleName)) { - if (Info->symbolizeData(ModuleOffset, Name, Start, Size) && Opts.Demangle) - Name = DemangleName(Name); - } - } - std::stringstream ss; - ss << Name << "\n" << Start << " " << Size << "\n"; - return ss.str(); -} - -void LLVMSymbolizer::flush() { - DeleteContainerSeconds(Modules); - ObjectPairForPathArch.clear(); - ObjectFileForArch.clear(); -} - -// 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(); -} - -static bool checkFileCRC(StringRef Path, uint32_t CRCHash) { - ErrorOr<std::unique_ptr<MemoryBuffer>> MB = - MemoryBuffer::getFileOrSTDIN(Path); - if (!MB) - return false; - return !zlib::isAvailable() || CRCHash == zlib::crc32(MB.get()->getBuffer()); -} - -static bool findDebugBinary(const std::string &OrigPath, - const std::string &DebuglinkName, uint32_t CRCHash, - std::string &Result) { - std::string OrigRealPath = OrigPath; -#if defined(HAVE_REALPATH) - if (char *RP = realpath(OrigPath.c_str(), nullptr)) { - OrigRealPath = RP; - free(RP); - } -#endif - SmallString<16> OrigDir(OrigRealPath); - llvm::sys::path::remove_filename(OrigDir); - SmallString<16> DebugPath = OrigDir; - // Try /path/to/original_binary/debuglink_name - llvm::sys::path::append(DebugPath, DebuglinkName); - if (checkFileCRC(DebugPath, CRCHash)) { - Result = DebugPath.str(); - return true; - } - // Try /path/to/original_binary/.debug/debuglink_name - DebugPath = OrigRealPath; - llvm::sys::path::append(DebugPath, ".debug", DebuglinkName); - if (checkFileCRC(DebugPath, CRCHash)) { - Result = DebugPath.str(); - return true; - } - // Try /usr/lib/debug/path/to/original_binary/debuglink_name - DebugPath = "/usr/lib/debug"; - llvm::sys::path::append(DebugPath, llvm::sys::path::relative_path(OrigDir), - DebuglinkName); - if (checkFileCRC(DebugPath, CRCHash)) { - Result = DebugPath.str(); - return true; - } - return false; -} - -static bool getGNUDebuglinkContents(const ObjectFile *Obj, std::string &DebugName, - uint32_t &CRCHash) { - if (!Obj) - return false; - for (const SectionRef &Section : Obj->sections()) { - StringRef Name; - Section.getName(Name); - Name = Name.substr(Name.find_first_not_of("._")); - if (Name == "gnu_debuglink") { - StringRef Data; - Section.getContents(Data); - DataExtractor DE(Data, Obj->isLittleEndian(), 0); - uint32_t Offset = 0; - if (const char *DebugNameStr = DE.getCStr(&Offset)) { - // 4-byte align the offset. - Offset = (Offset + 3) & ~0x3; - if (DE.isValidOffsetForDataOfSize(Offset, 4)) { - DebugName = DebugNameStr; - CRCHash = DE.getU32(&Offset); - return true; - } - } - break; - } - } - return false; -} - -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; - ObjectFile *Obj = nullptr; - ObjectFile *DbgObj = nullptr; - ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(Path); - if (!error(BinaryOrErr.getError())) { - 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 (!DbgObj) { - std::string DebuglinkName; - uint32_t CRCHash; - std::string DebugBinaryPath; - if (getGNUDebuglinkContents(Obj, DebuglinkName, CRCHash) && - findDebugBinary(Path, DebuglinkName, CRCHash, DebugBinaryPath)) { - BinaryOrErr = createBinary(DebugBinaryPath); - if (!error(BinaryOrErr.getError())) { - OwningBinary<Binary> B = std::move(BinaryOrErr.get()); - DbgObj = getObjectFileFromBinary(B.getBinary(), ArchName); - addOwningBinary(std::move(B)); - } - } - } - } - 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) { - if (!Bin) - return nullptr; - ObjectFile *Res = nullptr; - if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(Bin)) { - const auto &I = ObjectFileForArch.find( - std::make_pair(UB, ArchName)); - if (I != ObjectFileForArch.end()) - return I->second; - ErrorOr<std::unique_ptr<ObjectFile>> ParsedObj = - UB->getObjectForArch(ArchName); - if (ParsedObj) { - Res = ParsedObj.get().get(); - ParsedBinariesAndObjects.push_back(std::move(ParsedObj.get())); - } - ObjectFileForArch[std::make_pair(UB, ArchName)] = Res; - } else if (Bin->isObject()) { - Res = cast<ObjectFile>(Bin); - } - return Res; -} - -ModuleInfo * -LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) { - const auto &I = Modules.find(ModuleName); - if (I != Modules.end()) - return I->second; - std::string BinaryName = ModuleName; - std::string ArchName = Opts.DefaultArch; - size_t ColonPos = ModuleName.find_last_of(':'); - // Verify that substring after colon form a valid arch name. - if (ColonPos != std::string::npos) { - std::string ArchStr = ModuleName.substr(ColonPos + 1); - if (Triple(ArchStr).getArch() != Triple::UnknownArch) { - BinaryName = ModuleName.substr(0, ColonPos); - ArchName = ArchStr; - } - } - ObjectPair Objects = getOrCreateObjects(BinaryName, ArchName); - - if (!Objects.first) { - // Failed to find valid object file. - Modules.insert(make_pair(ModuleName, (ModuleInfo *)nullptr)); - return nullptr; - } - DIContext *Context = nullptr; - if (auto CoffObject = dyn_cast<COFFObjectFile>(Objects.first)) { - // If this is a COFF object, assume it contains PDB debug information. If - // we don't find any we will fall back to the DWARF case. - std::unique_ptr<IPDBSession> Session; - PDB_ErrorCode Error = loadDataForEXE(PDB_ReaderType::DIA, - Objects.first->getFileName(), Session); - if (Error == PDB_ErrorCode::Success) { - Context = new PDBContext(*CoffObject, std::move(Session), - Opts.RelativeAddresses); - } - } - if (!Context) - Context = new DWARFContextInMemory(*Objects.second); - assert(Context); - ModuleInfo *Info = new ModuleInfo(Objects.first, Context); - Modules.insert(make_pair(ModuleName, Info)); - return Info; -} - -std::string LLVMSymbolizer::printDILineInfo(DILineInfo LineInfo) const { - // By default, DILineInfo contains "<invalid>" for function/filename it - // cannot fetch. We replace it to "??" to make our output closer to addr2line. - static const std::string kDILineInfoBadString = "<invalid>"; - std::stringstream Result; - if (Opts.PrintFunctions != FunctionNameKind::None) { - std::string FunctionName = LineInfo.FunctionName; - if (FunctionName == kDILineInfoBadString) - FunctionName = kBadString; - else if (Opts.Demangle) - FunctionName = DemangleName(FunctionName); - Result << FunctionName << "\n"; - } - std::string Filename = LineInfo.FileName; - if (Filename == kDILineInfoBadString) - Filename = kBadString; - Result << Filename << ":" << LineInfo.Line << ":" << LineInfo.Column << "\n"; - return Result.str(); -} - -#if !defined(_MSC_VER) -// Assume that __cxa_demangle is provided by libcxxabi (except for Windows). -extern "C" char *__cxa_demangle(const char *mangled_name, char *output_buffer, - size_t *length, int *status); -#endif - -std::string LLVMSymbolizer::DemangleName(const std::string &Name) { -#if !defined(_MSC_VER) - // We can spoil names of symbols with C linkage, so use an heuristic - // approach to check if the name should be demangled. - if (Name.substr(0, 2) != "_Z") - return Name; - int status = 0; - char *DemangledName = __cxa_demangle(Name.c_str(), nullptr, nullptr, &status); - if (status != 0) - return Name; - std::string Result = DemangledName; - free(DemangledName); - return Result; -#else - char DemangledName[1024] = {0}; - DWORD result = ::UnDecorateSymbolName( - Name.c_str(), DemangledName, 1023, - UNDNAME_NO_ACCESS_SPECIFIERS | // Strip public, private, protected - UNDNAME_NO_ALLOCATION_LANGUAGE | // Strip __thiscall, __stdcall, etc - UNDNAME_NO_THROW_SIGNATURES | // Strip throw() specifications - UNDNAME_NO_MEMBER_TYPE | // Strip virtual, static, etc specifiers - UNDNAME_NO_MS_KEYWORDS | // Strip all MS extension keywords - UNDNAME_NO_FUNCTION_RETURNS); // Strip function return types - - return (result == 0) ? Name : std::string(DemangledName); -#endif -} - -} // namespace symbolize -} // namespace llvm diff --git a/tools/llvm-symbolizer/LLVMSymbolize.h b/tools/llvm-symbolizer/LLVMSymbolize.h deleted file mode 100644 index be246c3f8712..000000000000 --- a/tools/llvm-symbolizer/LLVMSymbolize.h +++ /dev/null @@ -1,144 +0,0 @@ -//===-- LLVMSymbolize.h ----------------------------------------- C++ -----===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Header for LLVM symbolization library. -// -//===----------------------------------------------------------------------===// -#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> -#include <string> - -namespace llvm { - -typedef DILineInfoSpecifier::FunctionNameKind FunctionNameKind; -using namespace object; - -namespace symbolize { - -class ModuleInfo; - -class LLVMSymbolizer { -public: - struct Options { - FunctionNameKind PrintFunctions; - bool UseSymbolTable : 1; - bool PrintInlining : 1; - bool Demangle : 1; - bool RelativeAddresses : 1; - std::string DefaultArch; - std::vector<std::string> DsymHints; - Options(FunctionNameKind PrintFunctions = FunctionNameKind::LinkageName, - bool UseSymbolTable = true, bool PrintInlining = true, - bool Demangle = true, bool RelativeAddresses = false, - std::string DefaultArch = "") - : PrintFunctions(PrintFunctions), UseSymbolTable(UseSymbolTable), - PrintInlining(PrintInlining), Demangle(Demangle), - RelativeAddresses(RelativeAddresses), DefaultArch(DefaultArch) {} - }; - - LLVMSymbolizer(const Options &Opts = Options()) : Opts(Opts) {} - ~LLVMSymbolizer() { - flush(); - } - - // Returns the result of symbolization for module name/offset as - // a string (possibly containing newlines). - std::string - symbolizeCode(const std::string &ModuleName, uint64_t ModuleOffset); - std::string - symbolizeData(const std::string &ModuleName, uint64_t ModuleOffset); - void flush(); - static std::string DemangleName(const std::string &Name); -private: - typedef std::pair<ObjectFile*, ObjectFile*> ObjectPair; - - ModuleInfo *getOrCreateModuleInfo(const std::string &ModuleName); - 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); - - std::string printDILineInfo(DILineInfo LineInfo) const; - - // 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. - 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[]; -}; - -class ModuleInfo { -public: - ModuleInfo(ObjectFile *Obj, DIContext *DICtx); - - DILineInfo symbolizeCode(uint64_t ModuleOffset, - const LLVMSymbolizer::Options &Opts) const; - DIInliningInfo symbolizeInlinedCode( - uint64_t ModuleOffset, const LLVMSymbolizer::Options &Opts) const; - bool symbolizeData(uint64_t ModuleOffset, std::string &Name, uint64_t &Start, - uint64_t &Size) const; - -private: - bool getNameFromSymbolTable(SymbolRef::Type Type, uint64_t Address, - std::string &Name, uint64_t &Addr, - uint64_t &Size) const; - // 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, uint64_t SymbolSize, - DataExtractor *OpdExtractor = nullptr, - uint64_t OpdAddress = 0); - ObjectFile *Module; - std::unique_ptr<DIContext> DebugInfoContext; - - struct SymbolDesc { - uint64_t Addr; - // If size is 0, assume that symbol occupies the whole memory range up to - // the following symbol. - uint64_t Size; - friend bool operator<(const SymbolDesc &s1, const SymbolDesc &s2) { - return s1.Addr < s2.Addr; - } - }; - std::map<SymbolDesc, StringRef> Functions; - std::map<SymbolDesc, StringRef> Objects; -}; - -} // namespace symbolize -} // namespace llvm - -#endif diff --git a/tools/llvm-symbolizer/Makefile b/tools/llvm-symbolizer/Makefile index de75befb1c97..8272d61280c7 100644 --- a/tools/llvm-symbolizer/Makefile +++ b/tools/llvm-symbolizer/Makefile @@ -9,7 +9,7 @@ LEVEL := ../.. TOOLNAME := llvm-symbolizer -LINK_COMPONENTS := DebugInfoDWARF DebugInfoPDB Object +LINK_COMPONENTS := DebugInfoDWARF DebugInfoPDB Object Support Symbolize # This tool has no plugins, optimize startup time. TOOL_NO_EXPORTS := 1 diff --git a/tools/llvm-symbolizer/llvm-symbolizer.cpp b/tools/llvm-symbolizer/llvm-symbolizer.cpp index 9c9f3adbf60e..e45660c84c77 100644 --- a/tools/llvm-symbolizer/llvm-symbolizer.cpp +++ b/tools/llvm-symbolizer/llvm-symbolizer.cpp @@ -15,8 +15,9 @@ // //===----------------------------------------------------------------------===// -#include "LLVMSymbolize.h" #include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/Symbolize/DIPrinter.h" +#include "llvm/DebugInfo/Symbolize/Symbolize.h" #include "llvm/Support/COM.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" @@ -73,6 +74,20 @@ 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 cl::opt<bool> + ClPrintAddress("print-address", cl::init(false), + cl::desc("Show address before line information")); + +static cl::opt<bool> + ClPrettyPrint("pretty-print", cl::init(false), + cl::desc("Make the output more human friendly")); + +static bool error(std::error_code ec) { + if (!ec) + return false; + errs() << "LLVMSymbolizer: error reading file: " << ec.message() << ".\n"; + return true; +} static bool parseCommand(bool &IsData, std::string &ModuleName, uint64_t &ModuleOffset) { @@ -118,9 +133,7 @@ static bool parseCommand(bool &IsData, std::string &ModuleName, // Skip delimiters and parse module offset. pos += strspn(pos, kDelimiters); int offset_length = strcspn(pos, kDelimiters); - if (StringRef(pos, offset_length).getAsInteger(0, ModuleOffset)) - return false; - return true; + return !StringRef(pos, offset_length).getAsInteger(0, ModuleOffset); } int main(int argc, char **argv) { @@ -132,9 +145,9 @@ int main(int argc, char **argv) { llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded); cl::ParseCommandLineOptions(argc, argv, "llvm-symbolizer\n"); - LLVMSymbolizer::Options Opts(ClPrintFunctions, ClUseSymbolTable, - ClPrintInlining, ClDemangle, + LLVMSymbolizer::Options Opts(ClPrintFunctions, ClUseSymbolTable, ClDemangle, ClUseRelativeAddress, ClDefaultArch); + for (const auto &hint : ClDsymHint) { if (sys::path::extension(hint) == ".dSYM") { Opts.DsymHints.push_back(hint); @@ -148,11 +161,28 @@ int main(int argc, char **argv) { bool IsData = false; std::string ModuleName; uint64_t ModuleOffset; + DIPrinter Printer(outs(), ClPrintFunctions != FunctionNameKind::None, + ClPrettyPrint); + while (parseCommand(IsData, ModuleName, ModuleOffset)) { - std::string Result = - IsData ? Symbolizer.symbolizeData(ModuleName, ModuleOffset) - : Symbolizer.symbolizeCode(ModuleName, ModuleOffset); - outs() << Result << "\n"; + if (ClPrintAddress) { + outs() << "0x"; + outs().write_hex(ModuleOffset); + StringRef Delimiter = (ClPrettyPrint == true) ? ": " : "\n"; + outs() << Delimiter; + } + if (IsData) { + auto ResOrErr = Symbolizer.symbolizeData(ModuleName, ModuleOffset); + Printer << (error(ResOrErr.getError()) ? DIGlobal() : ResOrErr.get()); + } else if (ClPrintInlining) { + auto ResOrErr = Symbolizer.symbolizeInlinedCode(ModuleName, ModuleOffset); + Printer << (error(ResOrErr.getError()) ? DIInliningInfo() + : ResOrErr.get()); + } else { + auto ResOrErr = Symbolizer.symbolizeCode(ModuleName, ModuleOffset); + Printer << (error(ResOrErr.getError()) ? DILineInfo() : ResOrErr.get()); + } + outs() << "\n"; outs().flush(); } diff --git a/tools/lto/CMakeLists.txt b/tools/lto/CMakeLists.txt index c479fa954cb0..62447eec389b 100644 --- a/tools/lto/CMakeLists.txt +++ b/tools/lto/CMakeLists.txt @@ -19,3 +19,13 @@ add_llvm_library(LTO SHARED ${SOURCES}) install(FILES ${LLVM_MAIN_INCLUDE_DIR}/llvm-c/lto.h DESTINATION include/llvm-c) + +if (APPLE) + set(LTO_VERSION ${LLVM_VERSION_MAJOR}) + if(LLVM_LTO_VERSION_OFFSET) + math(EXPR LTO_VERSION "${LLVM_VERSION_MAJOR} + ${LLVM_LTO_VERSION_OFFSET}") + endif() + set_property(TARGET LTO APPEND_STRING PROPERTY + LINK_FLAGS + " -compatibility_version 1 -current_version ${LTO_VERSION}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}") +endif() diff --git a/tools/lto/lto.cpp b/tools/lto/lto.cpp index 5c712f18c9ee..d8f99c050a34 100644 --- a/tools/lto/lto.cpp +++ b/tools/lto/lto.cpp @@ -15,6 +15,8 @@ #include "llvm-c/lto.h" #include "llvm/ADT/STLExtras.h" #include "llvm/CodeGen/CommandFlags.h" +#include "llvm/IR/DiagnosticInfo.h" +#include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/LLVMContext.h" #include "llvm/LTO/LTOCodeGenerator.h" #include "llvm/LTO/LTOModule.h" @@ -43,6 +45,16 @@ static cl::opt<bool> DisableLTOVectorization("disable-lto-vectorization", cl::init(false), cl::desc("Do not run loop or slp vectorization during LTO")); +#ifdef NDEBUG +static bool VerifyByDefault = false; +#else +static bool VerifyByDefault = true; +#endif + +static cl::opt<bool> DisableVerify( + "disable-llvm-verifier", cl::init(!VerifyByDefault), + cl::desc("Don't run the LLVM verifier during the optimization pipeline")); + // Holds most recent error string. // *** Not thread safe *** static std::string sLastErrorString; @@ -54,6 +66,24 @@ static bool initialized = false; // Holds the command-line option parsing state of the LTO module. static bool parsedOptions = false; +static LLVMContext *LTOContext = nullptr; + +static void diagnosticHandler(const DiagnosticInfo &DI, void *Context) { + if (DI.getSeverity() != DS_Error) { + DiagnosticPrinterRawOStream DP(errs()); + DI.print(DP); + errs() << '\n'; + return; + } + sLastErrorString = ""; + { + raw_string_ostream Stream(sLastErrorString); + DiagnosticPrinterRawOStream DP(Stream); + DI.print(DP); + } + sLastErrorString += '\n'; +} + // Initialize the configured targets if they have not been initialized. static void lto_initialize() { if (!initialized) { @@ -69,21 +99,37 @@ static void lto_initialize() { InitializeAllAsmParsers(); InitializeAllAsmPrinters(); InitializeAllDisassemblers(); + + LTOContext = &getGlobalContext(); + LTOContext->setDiagnosticHandler(diagnosticHandler, nullptr, true); initialized = true; } } namespace { +static void handleLibLTODiagnostic(lto_codegen_diagnostic_severity_t Severity, + const char *Msg, void *) { + sLastErrorString = Msg; + sLastErrorString += "\n"; +} + // This derived class owns the native object file. This helps implement the // libLTO API semantics, which require that the code generator owns the object // file. struct LibLTOCodeGenerator : LTOCodeGenerator { - LibLTOCodeGenerator() {} + LibLTOCodeGenerator() : LTOCodeGenerator(*LTOContext) { + setDiagnosticHandler(handleLibLTODiagnostic, nullptr); } LibLTOCodeGenerator(std::unique_ptr<LLVMContext> Context) - : LTOCodeGenerator(std::move(Context)) {} + : LTOCodeGenerator(*Context), OwnedContext(std::move(Context)) { + setDiagnosticHandler(handleLibLTODiagnostic, nullptr); } + + // Reset the module first in case MergedModule is created in OwnedContext. + // Module must be destructed before its context gets destructed. + ~LibLTOCodeGenerator() { resetMergedModule(); } std::unique_ptr<MemoryBuffer> NativeObjectFile; + std::unique_ptr<LLVMContext> OwnedContext; }; } @@ -147,14 +193,21 @@ lto_module_is_object_file_in_memory_for_target(const void* mem, lto_module_t lto_module_create(const char* path) { lto_initialize(); llvm::TargetOptions Options = InitTargetOptionsFromCodeGenFlags(); - return wrap(LTOModule::createFromFile(path, Options, sLastErrorString)); + ErrorOr<std::unique_ptr<LTOModule>> M = + LTOModule::createFromFile(*LTOContext, path, Options); + if (!M) + return nullptr; + return wrap(M->release()); } lto_module_t lto_module_create_from_fd(int fd, const char *path, size_t size) { lto_initialize(); llvm::TargetOptions Options = InitTargetOptionsFromCodeGenFlags(); - return wrap( - LTOModule::createFromOpenFile(fd, path, size, Options, sLastErrorString)); + ErrorOr<std::unique_ptr<LTOModule>> M = + LTOModule::createFromOpenFile(*LTOContext, fd, path, size, Options); + if (!M) + return nullptr; + return wrap(M->release()); } lto_module_t lto_module_create_from_fd_at_offset(int fd, const char *path, @@ -163,14 +216,21 @@ lto_module_t lto_module_create_from_fd_at_offset(int fd, const char *path, off_t offset) { lto_initialize(); llvm::TargetOptions Options = InitTargetOptionsFromCodeGenFlags(); - return wrap(LTOModule::createFromOpenFileSlice(fd, path, map_size, offset, - Options, sLastErrorString)); + ErrorOr<std::unique_ptr<LTOModule>> M = LTOModule::createFromOpenFileSlice( + *LTOContext, fd, path, map_size, offset, Options); + if (!M) + return nullptr; + return wrap(M->release()); } lto_module_t lto_module_create_from_memory(const void* mem, size_t length) { lto_initialize(); llvm::TargetOptions Options = InitTargetOptionsFromCodeGenFlags(); - return wrap(LTOModule::createFromBuffer(mem, length, Options, sLastErrorString)); + ErrorOr<std::unique_ptr<LTOModule>> M = + LTOModule::createFromBuffer(*LTOContext, mem, length, Options); + if (!M) + return nullptr; + return wrap(M->release()); } lto_module_t lto_module_create_from_memory_with_path(const void* mem, @@ -178,16 +238,22 @@ lto_module_t lto_module_create_from_memory_with_path(const void* mem, const char *path) { lto_initialize(); llvm::TargetOptions Options = InitTargetOptionsFromCodeGenFlags(); - return wrap( - LTOModule::createFromBuffer(mem, length, Options, sLastErrorString, path)); + ErrorOr<std::unique_ptr<LTOModule>> M = + LTOModule::createFromBuffer(*LTOContext, mem, length, Options, path); + if (!M) + return nullptr; + return wrap(M->release()); } 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)); + ErrorOr<std::unique_ptr<LTOModule>> M = + LTOModule::createInLocalContext(mem, length, Options, path); + if (!M) + return nullptr; + return wrap(M->release()); } lto_module_t lto_module_create_in_codegen_context(const void *mem, @@ -196,8 +262,9 @@ lto_module_t lto_module_create_in_codegen_context(const void *mem, lto_code_gen_t cg) { lto_initialize(); llvm::TargetOptions Options = InitTargetOptionsFromCodeGenFlags(); - return wrap(LTOModule::createInContext(mem, length, Options, sLastErrorString, - path, &unwrap(cg)->getContext())); + ErrorOr<std::unique_ptr<LTOModule>> M = LTOModule::createInContext( + mem, length, Options, path, &unwrap(cg)->getContext()); + return wrap(M->release()); } void lto_module_dispose(lto_module_t mod) { delete unwrap(mod); } @@ -260,7 +327,7 @@ bool lto_codegen_add_module(lto_code_gen_t cg, lto_module_t mod) { } void lto_codegen_set_module(lto_code_gen_t cg, lto_module_t mod) { - unwrap(cg)->setModule(unwrap(mod)); + unwrap(cg)->setModule(std::unique_ptr<LTOModule>(unwrap(mod))); } bool lto_codegen_set_debug_model(lto_code_gen_t cg, lto_debug_model debug) { @@ -269,8 +336,22 @@ bool lto_codegen_set_debug_model(lto_code_gen_t cg, lto_debug_model debug) { } bool lto_codegen_set_pic_model(lto_code_gen_t cg, lto_codegen_model model) { - unwrap(cg)->setCodePICModel(model); - return false; + switch (model) { + case LTO_CODEGEN_PIC_MODEL_STATIC: + unwrap(cg)->setCodePICModel(Reloc::Static); + return false; + case LTO_CODEGEN_PIC_MODEL_DYNAMIC: + unwrap(cg)->setCodePICModel(Reloc::PIC_); + return false; + case LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC: + unwrap(cg)->setCodePICModel(Reloc::DynamicNoPIC); + return false; + case LTO_CODEGEN_PIC_MODEL_DEFAULT: + unwrap(cg)->setCodePICModel(Reloc::Default); + return false; + } + sLastErrorString = "Unknown PIC model"; + return true; } void lto_codegen_set_cpu(lto_code_gen_t cg, const char *cpu) { @@ -301,14 +382,15 @@ static void maybeParseOptions(lto_code_gen_t cg) { bool lto_codegen_write_merged_modules(lto_code_gen_t cg, const char *path) { maybeParseOptions(cg); - return !unwrap(cg)->writeMergedModules(path, sLastErrorString); + return !unwrap(cg)->writeMergedModules(path); } const void *lto_codegen_compile(lto_code_gen_t cg, size_t *length) { maybeParseOptions(cg); LibLTOCodeGenerator *CG = unwrap(cg); - CG->NativeObjectFile = CG->compile(DisableInline, DisableGVNLoadPRE, - DisableLTOVectorization, sLastErrorString); + CG->NativeObjectFile = + CG->compile(DisableVerify, DisableInline, DisableGVNLoadPRE, + DisableLTOVectorization); if (!CG->NativeObjectFile) return nullptr; *length = CG->NativeObjectFile->getBufferSize(); @@ -317,15 +399,14 @@ const void *lto_codegen_compile(lto_code_gen_t cg, size_t *length) { bool lto_codegen_optimize(lto_code_gen_t cg) { maybeParseOptions(cg); - return !unwrap(cg)->optimize(DisableInline, - DisableGVNLoadPRE, DisableLTOVectorization, - sLastErrorString); + return !unwrap(cg)->optimize(DisableVerify, DisableInline, DisableGVNLoadPRE, + DisableLTOVectorization); } const void *lto_codegen_compile_optimized(lto_code_gen_t cg, size_t *length) { maybeParseOptions(cg); LibLTOCodeGenerator *CG = unwrap(cg); - CG->NativeObjectFile = CG->compileOptimized(sLastErrorString); + CG->NativeObjectFile = CG->compileOptimized(); if (!CG->NativeObjectFile) return nullptr; *length = CG->NativeObjectFile->getBufferSize(); @@ -335,8 +416,8 @@ const void *lto_codegen_compile_optimized(lto_code_gen_t cg, size_t *length) { bool lto_codegen_compile_to_file(lto_code_gen_t cg, const char **name) { maybeParseOptions(cg); return !unwrap(cg)->compile_to_file( - name, DisableInline, DisableGVNLoadPRE, - DisableLTOVectorization, sLastErrorString); + name, DisableVerify, DisableInline, DisableGVNLoadPRE, + DisableLTOVectorization); } void lto_codegen_debug_options(lto_code_gen_t cg, const char *opt) { diff --git a/tools/macho-dump/CMakeLists.txt b/tools/macho-dump/CMakeLists.txt deleted file mode 100644 index bc2dfbf427c3..000000000000 --- a/tools/macho-dump/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -set(LLVM_LINK_COMPONENTS - Object - Support - ) - -add_llvm_tool(macho-dump - macho-dump.cpp - ) diff --git a/tools/macho-dump/macho-dump.cpp b/tools/macho-dump/macho-dump.cpp deleted file mode 100644 index 39c2860df355..000000000000 --- a/tools/macho-dump/macho-dump.cpp +++ /dev/null @@ -1,434 +0,0 @@ -//===-- macho-dump.cpp - Mach Object Dumping Tool -------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This is a testing tool for use with the MC/Mach-O LLVM components. -// -//===----------------------------------------------------------------------===// - -#include "llvm/Object/MachO.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/ADT/Twine.h" -#include "llvm/Support/Casting.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Format.h" -#include "llvm/Support/ManagedStatic.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/raw_ostream.h" -#include <system_error> -using namespace llvm; -using namespace llvm::object; - -static cl::opt<std::string> -InputFile(cl::Positional, cl::desc("<input file>"), cl::init("-")); - -static cl::opt<bool> -ShowSectionData("dump-section-data", cl::desc("Dump the contents of sections"), - cl::init(false)); - -/// - -static const char *ProgramName; - -static void Message(const char *Type, const Twine &Msg) { - errs() << ProgramName << ": " << Type << ": " << Msg << "\n"; -} - -static int Error(const Twine &Msg) { - Message("error", Msg); - return 1; -} - -static void Warning(const Twine &Msg) { - Message("warning", Msg); -} - -/// - -static void DumpSegmentCommandData(StringRef Name, - uint64_t VMAddr, uint64_t VMSize, - uint64_t FileOffset, uint64_t FileSize, - uint32_t MaxProt, uint32_t InitProt, - uint32_t NumSections, uint32_t Flags) { - outs() << " ('segment_name', '"; - outs().write_escaped(Name, /*UseHexEscapes=*/true) << "')\n"; - outs() << " ('vm_addr', " << VMAddr << ")\n"; - outs() << " ('vm_size', " << VMSize << ")\n"; - outs() << " ('file_offset', " << FileOffset << ")\n"; - outs() << " ('file_size', " << FileSize << ")\n"; - outs() << " ('maxprot', " << MaxProt << ")\n"; - outs() << " ('initprot', " << InitProt << ")\n"; - outs() << " ('num_sections', " << NumSections << ")\n"; - outs() << " ('flags', " << Flags << ")\n"; -} - -static int DumpSectionData(const MachOObjectFile &Obj, unsigned Index, - StringRef Name, - StringRef SegmentName, uint64_t Address, - uint64_t Size, uint32_t Offset, - uint32_t Align, uint32_t RelocationTableOffset, - uint32_t NumRelocationTableEntries, - uint32_t Flags, uint32_t Reserved1, - uint32_t Reserved2, uint64_t Reserved3 = ~0ULL) { - outs() << " # Section " << Index << "\n"; - outs() << " (('section_name', '"; - outs().write_escaped(Name, /*UseHexEscapes=*/true) << "')\n"; - outs() << " ('segment_name', '"; - outs().write_escaped(SegmentName, /*UseHexEscapes=*/true) << "')\n"; - outs() << " ('address', " << Address << ")\n"; - outs() << " ('size', " << Size << ")\n"; - outs() << " ('offset', " << Offset << ")\n"; - outs() << " ('alignment', " << Align << ")\n"; - outs() << " ('reloc_offset', " << RelocationTableOffset << ")\n"; - outs() << " ('num_reloc', " << NumRelocationTableEntries << ")\n"; - outs() << " ('flags', " << format("0x%x", Flags) << ")\n"; - outs() << " ('reserved1', " << Reserved1 << ")\n"; - outs() << " ('reserved2', " << Reserved2 << ")\n"; - if (Reserved3 != ~0ULL) - outs() << " ('reserved3', " << Reserved3 << ")\n"; - outs() << " ),\n"; - - // Dump the relocation entries. - outs() << " ('_relocations', [\n"; - unsigned RelNum = 0; - for (relocation_iterator I = Obj.section_rel_begin(Index), - E = Obj.section_rel_end(Index); - I != E; ++I, ++RelNum) { - MachO::any_relocation_info RE = Obj.getRelocation(I->getRawDataRefImpl()); - outs() << " # Relocation " << RelNum << "\n"; - outs() << " (('word-0', " << format("0x%x", RE.r_word0) << "),\n"; - outs() << " ('word-1', " << format("0x%x", RE.r_word1) << ")),\n"; - } - outs() << " ])\n"; - - // Dump the section data, if requested. - if (ShowSectionData) { - outs() << " ('_section_data', '"; - StringRef Data = Obj.getData().substr(Offset, Size); - for (unsigned i = 0; i != Data.size(); ++i) { - if (i && (i % 4) == 0) - outs() << ' '; - outs() << hexdigit((Data[i] >> 4) & 0xF, /*LowerCase=*/true); - outs() << hexdigit((Data[i] >> 0) & 0xF, /*LowerCase=*/true); - } - outs() << "')\n"; - } - - return 0; -} - -static int DumpSegmentCommand(const MachOObjectFile &Obj, - const MachOObjectFile::LoadCommandInfo &LCI) { - MachO::segment_command SLC = Obj.getSegmentLoadCommand(LCI); - - DumpSegmentCommandData(StringRef(SLC.segname, 16), SLC.vmaddr, - SLC.vmsize, SLC.fileoff, SLC.filesize, - SLC.maxprot, SLC.initprot, SLC.nsects, SLC.flags); - - // Dump the sections. - outs() << " ('sections', [\n"; - for (unsigned i = 0; i != SLC.nsects; ++i) { - MachO::section Sect = Obj.getSection(LCI, i); - DumpSectionData(Obj, i, StringRef(Sect.sectname, 16), - StringRef(Sect.segname, 16), Sect.addr, - Sect.size, Sect.offset, Sect.align, - Sect.reloff, Sect.nreloc, Sect.flags, - Sect.reserved1, Sect.reserved2); - } - outs() << " ])\n"; - - return 0; -} - -static int DumpSegment64Command(const MachOObjectFile &Obj, - const MachOObjectFile::LoadCommandInfo &LCI) { - MachO::segment_command_64 SLC = Obj.getSegment64LoadCommand(LCI); - DumpSegmentCommandData(StringRef(SLC.segname, 16), SLC.vmaddr, - SLC.vmsize, SLC.fileoff, SLC.filesize, - SLC.maxprot, SLC.initprot, SLC.nsects, SLC.flags); - - // Dump the sections. - outs() << " ('sections', [\n"; - for (unsigned i = 0; i != SLC.nsects; ++i) { - MachO::section_64 Sect = Obj.getSection64(LCI, i); - - DumpSectionData(Obj, i, StringRef(Sect.sectname, 16), - StringRef(Sect.segname, 16), Sect.addr, - Sect.size, Sect.offset, Sect.align, - Sect.reloff, Sect.nreloc, Sect.flags, - Sect.reserved1, Sect.reserved2, - Sect.reserved3); - } - outs() << " ])\n"; - - return 0; -} - -static void DumpSymbolTableEntryData(const MachOObjectFile &Obj, - unsigned Index, uint32_t StringIndex, - uint8_t Type, uint8_t SectionIndex, - uint16_t Flags, uint64_t Value, - StringRef StringTable) { - const char *Name = &StringTable.data()[StringIndex]; - outs() << " # Symbol " << Index << "\n"; - outs() << " (('n_strx', " << StringIndex << ")\n"; - outs() << " ('n_type', " << format("0x%x", Type) << ")\n"; - outs() << " ('n_sect', " << uint32_t(SectionIndex) << ")\n"; - outs() << " ('n_desc', " << Flags << ")\n"; - outs() << " ('n_value', " << Value << ")\n"; - outs() << " ('_string', '" << Name << "')\n"; - outs() << " ),\n"; -} - -static int DumpSymtabCommand(const MachOObjectFile &Obj) { - MachO::symtab_command SLC = Obj.getSymtabLoadCommand(); - - outs() << " ('symoff', " << SLC.symoff << ")\n"; - outs() << " ('nsyms', " << SLC.nsyms << ")\n"; - outs() << " ('stroff', " << SLC.stroff << ")\n"; - outs() << " ('strsize', " << SLC.strsize << ")\n"; - - // Dump the string data. - outs() << " ('_string_data', '"; - StringRef StringTable = Obj.getStringTableData(); - outs().write_escaped(StringTable, - /*UseHexEscapes=*/true) << "')\n"; - - // Dump the symbol table. - outs() << " ('_symbols', [\n"; - unsigned SymNum = 0; - for (const SymbolRef &Symbol : Obj.symbols()) { - DataRefImpl DRI = Symbol.getRawDataRefImpl(); - if (Obj.is64Bit()) { - MachO::nlist_64 STE = Obj.getSymbol64TableEntry(DRI); - DumpSymbolTableEntryData(Obj, SymNum, STE.n_strx, STE.n_type, - STE.n_sect, STE.n_desc, STE.n_value, - StringTable); - } else { - MachO::nlist STE = Obj.getSymbolTableEntry(DRI); - DumpSymbolTableEntryData(Obj, SymNum, STE.n_strx, STE.n_type, - STE.n_sect, STE.n_desc, STE.n_value, - StringTable); - } - SymNum++; - } - outs() << " ])\n"; - - return 0; -} - -static int DumpDysymtabCommand(const MachOObjectFile &Obj) { - MachO::dysymtab_command DLC = Obj.getDysymtabLoadCommand(); - - outs() << " ('ilocalsym', " << DLC.ilocalsym << ")\n"; - outs() << " ('nlocalsym', " << DLC.nlocalsym << ")\n"; - outs() << " ('iextdefsym', " << DLC.iextdefsym << ")\n"; - outs() << " ('nextdefsym', " << DLC.nextdefsym << ")\n"; - outs() << " ('iundefsym', " << DLC.iundefsym << ")\n"; - outs() << " ('nundefsym', " << DLC.nundefsym << ")\n"; - outs() << " ('tocoff', " << DLC.tocoff << ")\n"; - outs() << " ('ntoc', " << DLC.ntoc << ")\n"; - outs() << " ('modtaboff', " << DLC.modtaboff << ")\n"; - outs() << " ('nmodtab', " << DLC.nmodtab << ")\n"; - outs() << " ('extrefsymoff', " << DLC.extrefsymoff << ")\n"; - outs() << " ('nextrefsyms', " << DLC.nextrefsyms << ")\n"; - outs() << " ('indirectsymoff', " << DLC.indirectsymoff << ")\n"; - outs() << " ('nindirectsyms', " << DLC.nindirectsyms << ")\n"; - outs() << " ('extreloff', " << DLC.extreloff << ")\n"; - outs() << " ('nextrel', " << DLC.nextrel << ")\n"; - outs() << " ('locreloff', " << DLC.locreloff << ")\n"; - outs() << " ('nlocrel', " << DLC.nlocrel << ")\n"; - - // Dump the indirect symbol table. - outs() << " ('_indirect_symbols', [\n"; - for (unsigned i = 0; i != DLC.nindirectsyms; ++i) { - uint32_t ISTE = Obj.getIndirectSymbolTableEntry(DLC, i); - outs() << " # Indirect Symbol " << i << "\n"; - outs() << " (('symbol_index', " << format("0x%x", ISTE) << "),),\n"; - } - outs() << " ])\n"; - - return 0; -} - -static int -DumpLinkeditDataCommand(const MachOObjectFile &Obj, - const MachOObjectFile::LoadCommandInfo &LCI) { - MachO::linkedit_data_command LLC = Obj.getLinkeditDataLoadCommand(LCI); - outs() << " ('dataoff', " << LLC.dataoff << ")\n" - << " ('datasize', " << LLC.datasize << ")\n" - << " ('_addresses', [\n"; - - SmallVector<uint64_t, 8> Addresses; - Obj.ReadULEB128s(LLC.dataoff, Addresses); - for (unsigned i = 0, e = Addresses.size(); i != e; ++i) - outs() << " # Address " << i << '\n' - << " ('address', " << format("0x%x", Addresses[i]) << "),\n"; - - outs() << " ])\n"; - - return 0; -} - -static int -DumpDataInCodeDataCommand(const MachOObjectFile &Obj, - const MachOObjectFile::LoadCommandInfo &LCI) { - MachO::linkedit_data_command LLC = Obj.getLinkeditDataLoadCommand(LCI); - outs() << " ('dataoff', " << LLC.dataoff << ")\n" - << " ('datasize', " << LLC.datasize << ")\n" - << " ('_data_regions', [\n"; - - unsigned NumRegions = LLC.datasize / sizeof(MachO::data_in_code_entry); - for (unsigned i = 0; i < NumRegions; ++i) { - MachO::data_in_code_entry DICE= Obj.getDataInCodeTableEntry(LLC.dataoff, i); - outs() << " # DICE " << i << "\n" - << " ('offset', " << DICE.offset << ")\n" - << " ('length', " << DICE.length << ")\n" - << " ('kind', " << DICE.kind << ")\n"; - } - - outs() <<" ])\n"; - - return 0; -} - -static int -DumpLinkerOptionsCommand(const MachOObjectFile &Obj, - const MachOObjectFile::LoadCommandInfo &LCI) { - MachO::linker_option_command LOLC = Obj.getLinkerOptionLoadCommand(LCI); - outs() << " ('count', " << LOLC.count << ")\n" - << " ('_strings', [\n"; - - uint64_t DataSize = LOLC.cmdsize - sizeof(MachO::linker_option_command); - const char *P = LCI.Ptr + sizeof(MachO::linker_option_command); - StringRef Data(P, DataSize); - for (unsigned i = 0; i != LOLC.count; ++i) { - std::pair<StringRef,StringRef> Split = Data.split('\0'); - outs() << "\t\""; - outs().write_escaped(Split.first); - outs() << "\",\n"; - Data = Split.second; - } - outs() <<" ])\n"; - - return 0; -} - -static int -DumpVersionMin(const MachOObjectFile &Obj, - const MachOObjectFile::LoadCommandInfo &LCI) { - MachO::version_min_command VMLC = Obj.getVersionMinLoadCommand(LCI); - outs() << " ('version, " << VMLC.version << ")\n" - << " ('sdk, " << VMLC.sdk << ")\n"; - return 0; -} - -static int -DumpDylibID(const MachOObjectFile &Obj, - const MachOObjectFile::LoadCommandInfo &LCI) { - MachO::dylib_command DLLC = Obj.getDylibIDLoadCommand(LCI); - outs() << " ('install_name', '" << LCI.Ptr + DLLC.dylib.name << "')\n" - << " ('timestamp, " << DLLC.dylib.timestamp << ")\n" - << " ('cur_version, " << DLLC.dylib.current_version << ")\n" - << " ('compat_version, " << DLLC.dylib.compatibility_version << ")\n"; - return 0; -} - -static int DumpLoadCommand(const MachOObjectFile &Obj, - const MachOObjectFile::LoadCommandInfo &LCI) { - switch (LCI.C.cmd) { - case MachO::LC_SEGMENT: - return DumpSegmentCommand(Obj, LCI); - case MachO::LC_SEGMENT_64: - return DumpSegment64Command(Obj, LCI); - case MachO::LC_SYMTAB: - return DumpSymtabCommand(Obj); - case MachO::LC_DYSYMTAB: - return DumpDysymtabCommand(Obj); - case MachO::LC_CODE_SIGNATURE: - case MachO::LC_SEGMENT_SPLIT_INFO: - case MachO::LC_FUNCTION_STARTS: - return DumpLinkeditDataCommand(Obj, LCI); - case MachO::LC_DATA_IN_CODE: - return DumpDataInCodeDataCommand(Obj, LCI); - case MachO::LC_LINKER_OPTION: - return DumpLinkerOptionsCommand(Obj, LCI); - case MachO::LC_VERSION_MIN_IPHONEOS: - case MachO::LC_VERSION_MIN_MACOSX: - return DumpVersionMin(Obj, LCI); - case MachO::LC_ID_DYLIB: - return DumpDylibID(Obj, LCI); - default: - Warning("unknown load command: " + Twine(LCI.C.cmd)); - return 0; - } -} - -static int DumpLoadCommand(const MachOObjectFile &Obj, unsigned Index, - const MachOObjectFile::LoadCommandInfo &LCI) { - outs() << " # Load Command " << Index << "\n" - << " (('command', " << LCI.C.cmd << ")\n" - << " ('size', " << LCI.C.cmdsize << ")\n"; - int Res = DumpLoadCommand(Obj, LCI); - outs() << " ),\n"; - return Res; -} - -static void printHeader(const MachOObjectFile *Obj, - const MachO::mach_header &Header) { - outs() << "('cputype', " << Header.cputype << ")\n"; - outs() << "('cpusubtype', " << Header.cpusubtype << ")\n"; - outs() << "('filetype', " << Header.filetype << ")\n"; - outs() << "('num_load_commands', " << Header.ncmds << ")\n"; - outs() << "('load_commands_size', " << Header.sizeofcmds << ")\n"; - outs() << "('flag', " << Header.flags << ")\n"; - - // Print extended header if 64-bit. - if (Obj->is64Bit()) { - const MachO::mach_header_64 *Header64 = - reinterpret_cast<const MachO::mach_header_64 *>(&Header); - outs() << "('reserved', " << Header64->reserved << ")\n"; - } -} - -int main(int argc, char **argv) { - ProgramName = argv[0]; - llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. - - cl::ParseCommandLineOptions(argc, argv, "llvm Mach-O dumping tool\n"); - - ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(InputFile); - if (std::error_code EC = BinaryOrErr.getError()) - return Error("unable to read input: '" + EC.message() + "'"); - Binary &Binary = *BinaryOrErr.get().getBinary(); - - const MachOObjectFile *InputObject = dyn_cast<MachOObjectFile>(&Binary); - if (!InputObject) - return Error("Not a MachO object"); - - // Print the header - MachO::mach_header_64 Header64; - MachO::mach_header *Header = reinterpret_cast<MachO::mach_header*>(&Header64); - if (InputObject->is64Bit()) - Header64 = InputObject->getHeader64(); - else - *Header = InputObject->getHeader(); - printHeader(InputObject, *Header); - - // Print the load commands. - int Res = 0; - unsigned Index = 0; - outs() << "('load_commands', [\n"; - for (const auto &Load : InputObject->load_commands()) { - if (DumpLoadCommand(*InputObject, Index++, Load)) - break; - } - outs() << "])\n"; - - return Res; -} diff --git a/tools/obj2yaml/elf2yaml.cpp b/tools/obj2yaml/elf2yaml.cpp index f117a10d3822..f2b013807345 100644 --- a/tools/obj2yaml/elf2yaml.cpp +++ b/tools/obj2yaml/elf2yaml.cpp @@ -26,14 +26,15 @@ class ELFDumper { typedef typename object::ELFFile<ELFT>::Elf_Word Elf_Word; const object::ELFFile<ELFT> &Obj; + ArrayRef<Elf_Word> ShndxTable; - std::error_code dumpSymbol(const Elf_Sym *Sym, bool IsDynamic, - ELFYAML::Symbol &S); + std::error_code dumpSymbol(const Elf_Sym *Sym, const Elf_Shdr *SymTab, + StringRef StrTable, ELFYAML::Symbol &S); std::error_code dumpCommonSection(const Elf_Shdr *Shdr, ELFYAML::Section &S); std::error_code dumpCommonRelocationSection(const Elf_Shdr *Shdr, ELFYAML::RelocationSection &S); template <class RelT> - std::error_code dumpRelocation(const Elf_Shdr *Shdr, const RelT *Rel, + std::error_code dumpRelocation(const RelT *Rel, const Elf_Shdr *SymTab, ELFYAML::Relocation &R); ErrorOr<ELFYAML::RelocationSection *> dumpRelSection(const Elf_Shdr *Shdr); @@ -68,15 +69,26 @@ ErrorOr<ELFYAML::Object *> ELFDumper<ELFT>::dump() { Y->Header.Flags = Obj.getHeader()->e_flags; Y->Header.Entry = Obj.getHeader()->e_entry; + const Elf_Shdr *Symtab = nullptr; + // Dump sections for (const Elf_Shdr &Sec : Obj.sections()) { switch (Sec.sh_type) { case ELF::SHT_NULL: - case ELF::SHT_SYMTAB: case ELF::SHT_DYNSYM: case ELF::SHT_STRTAB: // Do not dump these sections. break; + case ELF::SHT_SYMTAB: + Symtab = &Sec; + break; + case ELF::SHT_SYMTAB_SHNDX: { + ErrorOr<ArrayRef<Elf_Word>> TableOrErr = Obj.getSHNDXTable(Sec); + if (std::error_code EC = TableOrErr.getError()) + return EC; + ShndxTable = *TableOrErr; + break; + } case ELF::SHT_RELA: { ErrorOr<ELFYAML::RelocationSection *> S = dumpRelaSection(&Sec); if (std::error_code EC = S.getError()) @@ -122,18 +134,24 @@ ErrorOr<ELFYAML::Object *> ELFDumper<ELFT>::dump() { } // Dump symbols + ErrorOr<StringRef> StrTableOrErr = Obj.getStringTableForSymtab(*Symtab); + if (std::error_code EC = StrTableOrErr.getError()) + return EC; + StringRef StrTable = *StrTableOrErr; + bool IsFirstSym = true; - for (auto SI = Obj.symbol_begin(), SE = Obj.symbol_end(); SI != SE; ++SI) { + for (const Elf_Sym &Sym : Obj.symbols(Symtab)) { if (IsFirstSym) { IsFirstSym = false; continue; } ELFYAML::Symbol S; - if (std::error_code EC = ELFDumper<ELFT>::dumpSymbol(SI, false, S)) + if (std::error_code EC = + ELFDumper<ELFT>::dumpSymbol(&Sym, Symtab, StrTable, S)) return EC; - switch (SI->getBinding()) + switch (Sym.getBinding()) { case ELF::STB_LOCAL: Y->Symbols.Local.push_back(S); @@ -153,19 +171,20 @@ ErrorOr<ELFYAML::Object *> ELFDumper<ELFT>::dump() { } template <class ELFT> -std::error_code ELFDumper<ELFT>::dumpSymbol(const Elf_Sym *Sym, bool IsDynamic, - ELFYAML::Symbol &S) { +std::error_code +ELFDumper<ELFT>::dumpSymbol(const Elf_Sym *Sym, const Elf_Shdr *SymTab, + StringRef StrTable, ELFYAML::Symbol &S) { S.Type = Sym->getType(); S.Value = Sym->st_value; S.Size = Sym->st_size; S.Other = Sym->st_other; - ErrorOr<StringRef> NameOrErr = Obj.getSymbolName(Sym, IsDynamic); + ErrorOr<StringRef> NameOrErr = Sym->getName(StrTable); if (std::error_code EC = NameOrErr.getError()) return EC; S.Name = NameOrErr.get(); - ErrorOr<const Elf_Shdr *> ShdrOrErr = Obj.getSection(&*Sym); + ErrorOr<const Elf_Shdr *> ShdrOrErr = Obj.getSection(Sym, SymTab, ShndxTable); if (std::error_code EC = ShdrOrErr.getError()) return EC; const Elf_Shdr *Shdr = *ShdrOrErr; @@ -182,18 +201,14 @@ std::error_code ELFDumper<ELFT>::dumpSymbol(const Elf_Sym *Sym, bool IsDynamic, template <class ELFT> template <class RelT> -std::error_code ELFDumper<ELFT>::dumpRelocation(const Elf_Shdr *Shdr, - const RelT *Rel, +std::error_code ELFDumper<ELFT>::dumpRelocation(const RelT *Rel, + const Elf_Shdr *SymTab, ELFYAML::Relocation &R) { R.Type = Rel->getType(Obj.isMips64EL()); R.Offset = Rel->r_offset; R.Addend = 0; - auto NamePair = Obj.getRelocationSymbol(Shdr, Rel); - if (!NamePair.first) - return obj2yaml_error::success; - - const Elf_Shdr *SymTab = NamePair.first; + const Elf_Sym *Sym = Obj.getRelocationSymbol(Rel, SymTab); ErrorOr<const Elf_Shdr *> StrTabSec = Obj.getSection(SymTab->sh_link); if (std::error_code EC = StrTabSec.getError()) return EC; @@ -202,7 +217,7 @@ std::error_code ELFDumper<ELFT>::dumpRelocation(const Elf_Shdr *Shdr, return EC; StringRef StrTab = *StrTabOrErr; - ErrorOr<StringRef> NameOrErr = NamePair.second->getName(StrTab); + ErrorOr<StringRef> NameOrErr = Sym->getName(StrTab); if (std::error_code EC = NameOrErr.getError()) return EC; R.Symbol = NameOrErr.get(); @@ -264,9 +279,14 @@ ELFDumper<ELFT>::dumpRelSection(const Elf_Shdr *Shdr) { if (std::error_code EC = dumpCommonRelocationSection(Shdr, *S)) return EC; + ErrorOr<const Elf_Shdr *> SymTabOrErr = Obj.getSection(Shdr->sh_link); + if (std::error_code EC = SymTabOrErr.getError()) + return EC; + const Elf_Shdr *SymTab = *SymTabOrErr; + for (auto RI = Obj.rel_begin(Shdr), RE = Obj.rel_end(Shdr); RI != RE; ++RI) { ELFYAML::Relocation R; - if (std::error_code EC = dumpRelocation(Shdr, &*RI, R)) + if (std::error_code EC = dumpRelocation(&*RI, SymTab, R)) return EC; S->Relocations.push_back(R); } @@ -283,10 +303,15 @@ ELFDumper<ELFT>::dumpRelaSection(const Elf_Shdr *Shdr) { if (std::error_code EC = dumpCommonRelocationSection(Shdr, *S)) return EC; + ErrorOr<const Elf_Shdr *> SymTabOrErr = Obj.getSection(Shdr->sh_link); + if (std::error_code EC = SymTabOrErr.getError()) + return EC; + const Elf_Shdr *SymTab = *SymTabOrErr; + for (auto RI = Obj.rela_begin(Shdr), RE = Obj.rela_end(Shdr); RI != RE; ++RI) { ELFYAML::Relocation R; - if (std::error_code EC = dumpRelocation(Shdr, &*RI, R)) + if (std::error_code EC = dumpRelocation(&*RI, SymTab, R)) return EC; R.Addend = RI->r_addend; S->Relocations.push_back(R); @@ -331,14 +356,12 @@ ErrorOr<ELFYAML::Group *> ELFDumper<ELFT>::dumpGroup(const Elf_Shdr *Shdr) { if (std::error_code EC = dumpCommonSection(Shdr, *S)) return EC; // Get sh_info which is the signature. - const Elf_Sym *symbol = Obj.getSymbol(Shdr->sh_info); - ErrorOr<const Elf_Shdr *> Symtab = Obj.getSection(Shdr->sh_link); - if (std::error_code EC = Symtab.getError()) - return EC; - ErrorOr<const Elf_Shdr *> StrTabSec = Obj.getSection((*Symtab)->sh_link); - if (std::error_code EC = StrTabSec.getError()) + ErrorOr<const Elf_Shdr *> SymtabOrErr = Obj.getSection(Shdr->sh_link); + if (std::error_code EC = SymtabOrErr.getError()) return EC; - ErrorOr<StringRef> StrTabOrErr = Obj.getStringTable(*StrTabSec); + const Elf_Shdr *Symtab = *SymtabOrErr; + const Elf_Sym *symbol = Obj.getSymbol(Symtab, Shdr->sh_info); + ErrorOr<StringRef> StrTabOrErr = Obj.getStringTableForSymtab(*Symtab); if (std::error_code EC = StrTabOrErr.getError()) return EC; StringRef StrTab = *StrTabOrErr; diff --git a/tools/obj2yaml/obj2yaml.cpp b/tools/obj2yaml/obj2yaml.cpp index b64096d75fa4..ee6284da6e41 100644 --- a/tools/obj2yaml/obj2yaml.cpp +++ b/tools/obj2yaml/obj2yaml.cpp @@ -29,9 +29,6 @@ static std::error_code dumpObject(const ObjectFile &Obj) { } static std::error_code dumpInput(StringRef File) { - if (File != "-" && !sys::fs::exists(File)) - return obj2yaml_error::file_not_found; - ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(File); if (std::error_code EC = BinaryOrErr.getError()) return EC; diff --git a/tools/opt/CMakeLists.txt b/tools/opt/CMakeLists.txt index 5f825220cc88..9194a6ba0985 100644 --- a/tools/opt/CMakeLists.txt +++ b/tools/opt/CMakeLists.txt @@ -4,7 +4,6 @@ set(LLVM_LINK_COMPONENTS BitWriter CodeGen Core - IPA IPO IRReader InstCombine diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index 0db60d144409..fe1605aa8436 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -28,6 +28,7 @@ #include "llvm/IR/DebugInfo.h" #include "llvm/IR/IRPrintingPasses.h" #include "llvm/IR/LLVMContext.h" +#include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/LegacyPassNameParser.h" #include "llvm/IR/Module.h" #include "llvm/IR/Verifier.h" @@ -36,7 +37,6 @@ #include "llvm/LinkAllIR.h" #include "llvm/LinkAllPasses.h" #include "llvm/MC/SubtargetFeature.h" -#include "llvm/IR/LegacyPassManager.h" #include "llvm/Support/Debug.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Host.h" @@ -51,6 +51,7 @@ #include "llvm/Support/ToolOutputFile.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/Utils/Cloning.h" #include <algorithm> #include <memory> using namespace llvm; @@ -190,6 +191,11 @@ static cl::opt<bool> PreserveAssemblyUseListOrder( cl::desc("Preserve use-list order when writing LLVM assembly."), cl::init(false), cl::Hidden); +static cl::opt<bool> + RunTwice("run-twice", + cl::desc("Run all passes twice, re-using the same pass manager."), + cl::init(false), cl::Hidden); + static inline void addPass(legacy::PassManagerBase &PM, Pass *P) { // Add the pass to the pass manager... PM.add(P); @@ -312,7 +318,6 @@ int main(int argc, char **argv) { initializeVectorization(Registry); initializeIPO(Registry); initializeAnalysis(Registry); - initializeIPA(Registry); initializeTransformUtils(Registry); initializeInstCombine(Registry); initializeInstrumentation(Registry); @@ -583,22 +588,61 @@ int main(int argc, char **argv) { if (!NoVerify && !VerifyEach) Passes.add(createVerifierPass()); + // In run twice mode, we want to make sure the output is bit-by-bit + // equivalent if we run the pass manager again, so setup two buffers and + // a stream to write to them. Note that llc does something similar and it + // may be worth to abstract this out in the future. + SmallVector<char, 0> Buffer; + SmallVector<char, 0> CompileTwiceBuffer; + std::unique_ptr<raw_svector_ostream> BOS; + raw_ostream *OS = nullptr; + // Write bitcode or assembly to the output as the last step... if (!NoOutput && !AnalyzeOnly) { + assert(Out); + OS = &Out->os(); + if (RunTwice) { + BOS = make_unique<raw_svector_ostream>(Buffer); + OS = BOS.get(); + } if (OutputAssembly) - Passes.add( - createPrintModulePass(Out->os(), "", PreserveAssemblyUseListOrder)); + Passes.add(createPrintModulePass(*OS, "", PreserveAssemblyUseListOrder)); else - Passes.add( - createBitcodeWriterPass(Out->os(), PreserveBitcodeUseListOrder)); + Passes.add(createBitcodeWriterPass(*OS, PreserveBitcodeUseListOrder)); } // Before executing passes, print the final values of the LLVM options. cl::PrintOptionValues(); + // If requested, run all passes again with the same pass manager to catch + // bugs caused by persistent state in the passes + if (RunTwice) { + std::unique_ptr<Module> M2(CloneModule(M.get())); + Passes.run(*M2); + CompileTwiceBuffer = Buffer; + Buffer.clear(); + } + // Now that we have all of the passes ready, run them. Passes.run(*M); + // Compare the two outputs and make sure they're the same + if (RunTwice) { + assert(Out); + if (Buffer.size() != CompileTwiceBuffer.size() || + (memcmp(Buffer.data(), CompileTwiceBuffer.data(), Buffer.size()) != + 0)) { + errs() << "Running the pass manager twice changed the output.\n" + "Writing the result of the second run to the specified output.\n" + "To generate the one-run comparison binary, just run without\n" + "the compile-twice option\n"; + Out->os() << BOS->str(); + Out->keep(); + return 1; + } + Out->os() << BOS->str(); + } + // Declare success. if (!NoOutput || PrintBreakpoints) Out->keep(); diff --git a/tools/sancov/CMakeLists.txt b/tools/sancov/CMakeLists.txt new file mode 100644 index 000000000000..e92b1fcbb862 --- /dev/null +++ b/tools/sancov/CMakeLists.txt @@ -0,0 +1,17 @@ +set(LLVM_LINK_COMPONENTS + AllTargetsAsmPrinters + AllTargetsDescs + AllTargetsDisassemblers + AllTargetsInfos + DebugInfoDWARF + DebugInfoPDB + MC + MCDisassembler + Object + Support + Symbolize + ) + +add_llvm_tool(sancov + sancov.cc + ) diff --git a/tools/sancov/Makefile b/tools/sancov/Makefile new file mode 100644 index 000000000000..7dba1a7a594a --- /dev/null +++ b/tools/sancov/Makefile @@ -0,0 +1,18 @@ +##===- tools/sancov/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 := sancov +LINK_COMPONENTS := all-targets DebugInfoDWARF DebugInfoPDB MC MCParser \ + MCDisassembler Object Support Symbolize + +# This tool has no plugins, optimize startup time. +TOOL_NO_EXPORTS := 1 + +include $(LEVEL)/Makefile.common diff --git a/tools/sancov/sancov.cc b/tools/sancov/sancov.cc new file mode 100644 index 000000000000..a07cdbe097a3 --- /dev/null +++ b/tools/sancov/sancov.cc @@ -0,0 +1,533 @@ +//===-- sancov.cc --------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a command-line tool for reading and analyzing sanitizer +// coverage. +//===----------------------------------------------------------------------===// +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/Symbolize/Symbolize.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/MCInstrInfo.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Object/Archive.h" +#include "llvm/Object/Binary.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/LineIterator.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/SpecialCaseList.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/raw_ostream.h" + +#include <set> +#include <stdio.h> +#include <string> +#include <vector> + +using namespace llvm; + +namespace { + +// --------- COMMAND LINE FLAGS --------- + +enum ActionType { + PrintAction, + CoveredFunctionsAction, + NotCoveredFunctionsAction +}; + +cl::opt<ActionType> Action( + cl::desc("Action (required)"), cl::Required, + cl::values(clEnumValN(PrintAction, "print", "Print coverage addresses"), + clEnumValN(CoveredFunctionsAction, "covered-functions", + "Print all covered funcions."), + clEnumValN(NotCoveredFunctionsAction, "not-covered-functions", + "Print all not covered funcions."), + clEnumValEnd)); + +static cl::list<std::string> ClInputFiles(cl::Positional, cl::OneOrMore, + cl::desc("<filenames...>")); + +static cl::opt<std::string> + ClBinaryName("obj", cl::Required, + cl::desc("Path to object file to be symbolized")); + +static cl::opt<bool> + ClDemangle("demangle", cl::init(true), + cl::desc("Print demangled function name.")); + +static cl::opt<std::string> ClStripPathPrefix( + "strip_path_prefix", cl::init(""), + cl::desc("Strip this prefix from file paths in reports.")); + +static cl::opt<std::string> + ClBlacklist("blacklist", cl::init(""), + cl::desc("Blacklist file (sanitizer blacklist format).")); + +static cl::opt<bool> ClUseDefaultBlacklist( + "use_default_blacklist", cl::init(true), cl::Hidden, + cl::desc("Controls if default blacklist should be used.")); + +static const char *const DefaultBlacklist = "fun:__sanitizer_*"; + +// --------- FORMAT SPECIFICATION --------- + +struct FileHeader { + uint32_t Bitness; + uint32_t Magic; +}; + +static const uint32_t BinCoverageMagic = 0xC0BFFFFF; +static const uint32_t Bitness32 = 0xFFFFFF32; +static const uint32_t Bitness64 = 0xFFFFFF64; + +// --------- + +static void FailIfError(std::error_code Error) { + if (!Error) + return; + errs() << "Error: " << Error.message() << "(" << Error.value() << ")\n"; + exit(1); +} + +template <typename T> static void FailIfError(const ErrorOr<T> &E) { + FailIfError(E.getError()); +} + +static void FailIfNotEmpty(const std::string &E) { + if (E.empty()) + return; + errs() << "Error: " << E << "\n"; + exit(1); +} + +template <typename T> +static void FailIfEmpty(const std::unique_ptr<T> &Ptr, + const std::string &Message) { + if (Ptr.get()) + return; + errs() << "Error: " << Message << "\n"; + exit(1); +} + +template <typename T> +static void readInts(const char *Start, const char *End, + std::set<uint64_t> *Ints) { + const T *S = reinterpret_cast<const T *>(Start); + const T *E = reinterpret_cast<const T *>(End); + std::copy(S, E, std::inserter(*Ints, Ints->end())); +} + +struct FileLoc { + bool operator<(const FileLoc &RHS) const { + return std::tie(FileName, Line) < std::tie(RHS.FileName, RHS.Line); + } + + std::string FileName; + uint32_t Line; +}; + +struct FunctionLoc { + bool operator<(const FunctionLoc &RHS) const { + return std::tie(Loc, FunctionName) < std::tie(RHS.Loc, RHS.FunctionName); + } + + FileLoc Loc; + std::string FunctionName; +}; + +std::string stripPathPrefix(std::string Path) { + if (ClStripPathPrefix.empty()) + return Path; + size_t Pos = Path.find(ClStripPathPrefix); + if (Pos == std::string::npos) + return Path; + return Path.substr(Pos + ClStripPathPrefix.size()); +} + +// Compute [FileLoc -> FunctionName] map for given addresses. +static std::map<FileLoc, std::string> +computeFunctionsMap(const std::set<uint64_t> &Addrs) { + std::map<FileLoc, std::string> Fns; + + symbolize::LLVMSymbolizer::Options SymbolizerOptions; + SymbolizerOptions.Demangle = ClDemangle; + SymbolizerOptions.UseSymbolTable = true; + symbolize::LLVMSymbolizer Symbolizer(SymbolizerOptions); + + // Fill in Fns map. + for (auto Addr : Addrs) { + auto InliningInfo = Symbolizer.symbolizeInlinedCode(ClBinaryName, Addr); + FailIfError(InliningInfo); + for (uint32_t I = 0; I < InliningInfo->getNumberOfFrames(); ++I) { + auto FrameInfo = InliningInfo->getFrame(I); + SmallString<256> FileName(FrameInfo.FileName); + sys::path::remove_dots(FileName, /* remove_dot_dot */ true); + FileLoc Loc = {FileName.str(), FrameInfo.Line}; + Fns[Loc] = FrameInfo.FunctionName; + } + } + + return Fns; +} + +// Compute functions for given addresses. It keeps only the first +// occurence of a function within a file. +std::set<FunctionLoc> computeFunctionLocs(const std::set<uint64_t> &Addrs) { + std::map<FileLoc, std::string> Fns = computeFunctionsMap(Addrs); + + std::set<FunctionLoc> Result; + std::string LastFileName; + std::set<std::string> ProcessedFunctions; + + for (const auto &P : Fns) { + std::string FileName = P.first.FileName; + std::string FunctionName = P.second; + + if (LastFileName != FileName) + ProcessedFunctions.clear(); + LastFileName = FileName; + + if (!ProcessedFunctions.insert(FunctionName).second) + continue; + + Result.insert(FunctionLoc{P.first, P.second}); + } + + return Result; +} + +// Locate __sanitizer_cov* function addresses that are used for coverage +// reporting. +static std::set<uint64_t> +findSanitizerCovFunctions(const object::ObjectFile &O) { + std::set<uint64_t> Result; + + for (const object::SymbolRef &Symbol : O.symbols()) { + ErrorOr<uint64_t> AddressOrErr = Symbol.getAddress(); + FailIfError(AddressOrErr); + + ErrorOr<StringRef> NameOrErr = Symbol.getName(); + FailIfError(NameOrErr); + StringRef Name = NameOrErr.get(); + + if (Name == "__sanitizer_cov" || Name == "__sanitizer_cov_with_check" || + Name == "__sanitizer_cov_trace_func_enter") { + Result.insert(AddressOrErr.get()); + } + } + + if (Result.empty()) + FailIfNotEmpty("__sanitizer_cov* functions not found"); + + return Result; +} + +// Locate addresses of all coverage points in a file. Coverage point +// is defined as the 'address of instruction following __sanitizer_cov +// call - 1'. +static void getObjectCoveragePoints(const object::ObjectFile &O, + std::set<uint64_t> *Addrs) { + Triple TheTriple("unknown-unknown-unknown"); + TheTriple.setArch(Triple::ArchType(O.getArch())); + auto TripleName = TheTriple.getTriple(); + + std::string Error; + const Target *TheTarget = TargetRegistry::lookupTarget(TripleName, Error); + FailIfNotEmpty(Error); + + std::unique_ptr<const MCSubtargetInfo> STI( + TheTarget->createMCSubtargetInfo(TripleName, "", "")); + FailIfEmpty(STI, "no subtarget info for target " + TripleName); + + std::unique_ptr<const MCRegisterInfo> MRI( + TheTarget->createMCRegInfo(TripleName)); + FailIfEmpty(MRI, "no register info for target " + TripleName); + + std::unique_ptr<const MCAsmInfo> AsmInfo( + TheTarget->createMCAsmInfo(*MRI, TripleName)); + FailIfEmpty(AsmInfo, "no asm info for target " + TripleName); + + std::unique_ptr<const MCObjectFileInfo> MOFI(new MCObjectFileInfo); + MCContext Ctx(AsmInfo.get(), MRI.get(), MOFI.get()); + std::unique_ptr<MCDisassembler> DisAsm( + TheTarget->createMCDisassembler(*STI, Ctx)); + FailIfEmpty(DisAsm, "no disassembler info for target " + TripleName); + + std::unique_ptr<const MCInstrInfo> MII(TheTarget->createMCInstrInfo()); + FailIfEmpty(MII, "no instruction info for target " + TripleName); + + std::unique_ptr<const MCInstrAnalysis> MIA( + TheTarget->createMCInstrAnalysis(MII.get())); + FailIfEmpty(MIA, "no instruction analysis info for target " + TripleName); + + auto SanCovAddrs = findSanitizerCovFunctions(O); + + for (const auto Section : O.sections()) { + if (Section.isVirtual() || !Section.isText()) // llvm-objdump does the same. + continue; + uint64_t SectionAddr = Section.getAddress(); + uint64_t SectSize = Section.getSize(); + if (!SectSize) + continue; + + StringRef SectionName; + FailIfError(Section.getName(SectionName)); + + StringRef BytesStr; + FailIfError(Section.getContents(BytesStr)); + ArrayRef<uint8_t> Bytes(reinterpret_cast<const uint8_t *>(BytesStr.data()), + BytesStr.size()); + + for (uint64_t Index = 0, Size = 0; Index < Section.getSize(); + Index += Size) { + MCInst Inst; + if (!DisAsm->getInstruction(Inst, Size, Bytes.slice(Index), + SectionAddr + Index, nulls(), nulls())) { + if (Size == 0) + Size = 1; + continue; + } + uint64_t Target; + if (MIA->isCall(Inst) && + MIA->evaluateBranch(Inst, SectionAddr + Index, Size, Target)) { + if (SanCovAddrs.find(Target) != SanCovAddrs.end()) { + // Sanitizer coverage uses the address of the next instruction - 1. + Addrs->insert(Index + SectionAddr + Size - 1); + } + } + } + } +} + +static void getArchiveCoveragePoints(const object::Archive &A, + std::set<uint64_t> *Addrs) { + for (auto &ErrorOrChild : A.children()) { + FailIfError(ErrorOrChild); + const object::Archive::Child &C = *ErrorOrChild; + ErrorOr<std::unique_ptr<object::Binary>> ChildOrErr = C.getAsBinary(); + FailIfError(ChildOrErr); + if (object::ObjectFile *O = + dyn_cast<object::ObjectFile>(&*ChildOrErr.get())) + getObjectCoveragePoints(*O, Addrs); + else + FailIfError(object::object_error::invalid_file_type); + } +} + +// Locate addresses of all coverage points in a file. Coverage point +// is defined as the 'address of instruction following __sanitizer_cov +// call - 1'. +std::set<uint64_t> getCoveragePoints(std::string FileName) { + std::set<uint64_t> Result; + + ErrorOr<object::OwningBinary<object::Binary>> BinaryOrErr = + object::createBinary(FileName); + FailIfError(BinaryOrErr); + + object::Binary &Binary = *BinaryOrErr.get().getBinary(); + if (object::Archive *A = dyn_cast<object::Archive>(&Binary)) + getArchiveCoveragePoints(*A, &Result); + else if (object::ObjectFile *O = dyn_cast<object::ObjectFile>(&Binary)) + getObjectCoveragePoints(*O, &Result); + else + FailIfError(object::object_error::invalid_file_type); + + return Result; +} + +static std::unique_ptr<SpecialCaseList> createDefaultBlacklist() { + if (!ClUseDefaultBlacklist) + return std::unique_ptr<SpecialCaseList>(); + std::unique_ptr<MemoryBuffer> MB = + MemoryBuffer::getMemBuffer(DefaultBlacklist); + std::string Error; + auto Blacklist = SpecialCaseList::create(MB.get(), Error); + FailIfNotEmpty(Error); + return Blacklist; +} + +static std::unique_ptr<SpecialCaseList> createUserBlacklist() { + if (ClBlacklist.empty()) + return std::unique_ptr<SpecialCaseList>(); + + return SpecialCaseList::createOrDie({{ClBlacklist}}); +} + +static void printFunctionLocs(const std::set<FunctionLoc> &FnLocs, + raw_ostream &OS) { + std::unique_ptr<SpecialCaseList> DefaultBlacklist = createDefaultBlacklist(); + std::unique_ptr<SpecialCaseList> UserBlacklist = createUserBlacklist(); + + for (const FunctionLoc &FnLoc : FnLocs) { + if (DefaultBlacklist && + DefaultBlacklist->inSection("fun", FnLoc.FunctionName)) + continue; + if (DefaultBlacklist && + DefaultBlacklist->inSection("src", FnLoc.Loc.FileName)) + continue; + if (UserBlacklist && UserBlacklist->inSection("fun", FnLoc.FunctionName)) + continue; + if (UserBlacklist && UserBlacklist->inSection("src", FnLoc.Loc.FileName)) + continue; + + OS << stripPathPrefix(FnLoc.Loc.FileName) << ":" << FnLoc.Loc.Line << " " + << FnLoc.FunctionName << "\n"; + } +} + +class CoverageData { + public: + // Read single file coverage data. + static ErrorOr<std::unique_ptr<CoverageData>> read(std::string FileName) { + ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = + MemoryBuffer::getFile(FileName); + if (!BufOrErr) + return BufOrErr.getError(); + std::unique_ptr<MemoryBuffer> Buf = std::move(BufOrErr.get()); + if (Buf->getBufferSize() < 8) { + errs() << "File too small (<8): " << Buf->getBufferSize(); + return make_error_code(errc::illegal_byte_sequence); + } + const FileHeader *Header = + reinterpret_cast<const FileHeader *>(Buf->getBufferStart()); + + if (Header->Magic != BinCoverageMagic) { + errs() << "Wrong magic: " << Header->Magic; + return make_error_code(errc::illegal_byte_sequence); + } + + auto Addrs = llvm::make_unique<std::set<uint64_t>>(); + + switch (Header->Bitness) { + case Bitness64: + readInts<uint64_t>(Buf->getBufferStart() + 8, Buf->getBufferEnd(), + Addrs.get()); + break; + case Bitness32: + readInts<uint32_t>(Buf->getBufferStart() + 8, Buf->getBufferEnd(), + Addrs.get()); + break; + default: + errs() << "Unsupported bitness: " << Header->Bitness; + return make_error_code(errc::illegal_byte_sequence); + } + + return std::unique_ptr<CoverageData>(new CoverageData(std::move(Addrs))); + } + + // Merge multiple coverage data together. + static std::unique_ptr<CoverageData> + merge(const std::vector<std::unique_ptr<CoverageData>> &Covs) { + auto Addrs = llvm::make_unique<std::set<uint64_t>>(); + + for (const auto &Cov : Covs) + Addrs->insert(Cov->Addrs->begin(), Cov->Addrs->end()); + + return std::unique_ptr<CoverageData>(new CoverageData(std::move(Addrs))); + } + + // Read list of files and merges their coverage info. + static ErrorOr<std::unique_ptr<CoverageData>> + readAndMerge(const std::vector<std::string> &FileNames) { + std::vector<std::unique_ptr<CoverageData>> Covs; + for (const auto &FileName : FileNames) { + auto Cov = read(FileName); + if (!Cov) + return Cov.getError(); + Covs.push_back(std::move(Cov.get())); + } + return merge(Covs); + } + + // Print coverage addresses. + void printAddrs(raw_ostream &OS) { + for (auto Addr : *Addrs) { + OS << "0x"; + OS.write_hex(Addr); + OS << "\n"; + } + } + + // Print list of covered functions. + // Line format: <file_name>:<line> <function_name> + void printCoveredFunctions(raw_ostream &OS) { + printFunctionLocs(computeFunctionLocs(*Addrs), OS); + } + + // Print list of not covered functions. + // Line format: <file_name>:<line> <function_name> + void printNotCoveredFunctions(raw_ostream &OS) { + std::set<FunctionLoc> AllFns = + computeFunctionLocs(getCoveragePoints(ClBinaryName)); + std::set<FunctionLoc> CoveredFns = computeFunctionLocs(*Addrs); + + std::set<FunctionLoc> NotCoveredFns; + std::set_difference(AllFns.begin(), AllFns.end(), CoveredFns.begin(), + CoveredFns.end(), + std::inserter(NotCoveredFns, NotCoveredFns.end())); + printFunctionLocs(NotCoveredFns, OS); + } + +private: + explicit CoverageData(std::unique_ptr<std::set<uint64_t>> Addrs) + : Addrs(std::move(Addrs)) {} + + std::unique_ptr<std::set<uint64_t>> Addrs; +}; +} // namespace + +int main(int argc, char **argv) { + // Print stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + + llvm::InitializeAllTargetInfos(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllDisassemblers(); + + cl::ParseCommandLineOptions(argc, argv, "Sanitizer Coverage Processing Tool"); + + auto CovData = CoverageData::readAndMerge(ClInputFiles); + FailIfError(CovData); + + switch (Action) { + case PrintAction: { + CovData.get()->printAddrs(outs()); + return 0; + } + case CoveredFunctionsAction: { + CovData.get()->printCoveredFunctions(outs()); + return 0; + } + case NotCoveredFunctionsAction: { + CovData.get()->printNotCoveredFunctions(outs()); + return 0; + } + } + + llvm_unreachable("unsupported action"); +} diff --git a/tools/verify-uselistorder/verify-uselistorder.cpp b/tools/verify-uselistorder/verify-uselistorder.cpp index efa4bcbe1aaa..c3fec1326acb 100644 --- a/tools/verify-uselistorder/verify-uselistorder.cpp +++ b/tools/verify-uselistorder/verify-uselistorder.cpp @@ -346,6 +346,7 @@ static void verifyAfterRoundTrip(const Module &M, if (!matches(ValueMapping(M), ValueMapping(*OtherM))) report_fatal_error("use-list order changed"); } + static void verifyBitcodeUseListOrder(const Module &M) { TempFile F; if (F.init("bc")) diff --git a/tools/xcode-toolchain/CMakeLists.txt b/tools/xcode-toolchain/CMakeLists.txt new file mode 100644 index 000000000000..4eacbd320efc --- /dev/null +++ b/tools/xcode-toolchain/CMakeLists.txt @@ -0,0 +1,72 @@ +# OS X 10.11 El Capitan has just been released. One of the new features, System +# Integrity Protection, prevents modifying the base OS install, even with sudo. +# This prevents LLVM developers on OS X from being able to easily install new +# system compilers. The feature can be disabled, but to make it easier for +# developers to work without disabling SIP, this file can generate an Xcode +# toolchain. Xcode toolchains are a mostly-undocumented feature that allows +# multiple copies of low level tools to be installed to different locations, and +# users can easily switch between them. + +# Setting an environment variable TOOLCHAINS to the toolchain's identifier will +# result in /usr/bin/<tool> or xcrun <tool> to find the tool in the toolchain. + +# To make this work with Xcode 7.1 and later you can install the toolchain this +# file generates anywhere on your system and set EXTERNAL_TOOLCHAINS_DIR to the +# path specified by $CMAKE_INSTALL_PREFIX/Toolchains + +# This file generates a custom install-xcode-toolchain target which constructs +# and installs a toolchain with the identifier in the pattern: +# org.llvm.${PACKAGE_VERSION}. This toolchain can then be used to override the +# system compiler by setting TOOLCHAINS=org.llvm.${PACKAGE_VERSION} in the +# in the environment. + +# Example usage: +# cmake -G Ninja -DLLVM_CREATE_XCODE_TOOLCHAIN=On +# -DCMAKE_INSTALL_PREFIX=$PWD/install +# ninja install-xcode-toolchain +# export EXTERNAL_TOOLCHAINS_DIR=$PWD/install/Toolchains +# export TOOLCHAINS=org.llvm.3.8.0svn + +# `xcrun -find clang` should return the installed clang, and `clang --version` +# should show 3.8.0svn. + +if(NOT APPLE) + return() +endif() + +option(LLVM_CREATE_XCODE_TOOLCHAIN "Create a target to install LLVM into an Xcode toolchain" Off) + +if(NOT LLVM_CREATE_XCODE_TOOLCHAIN) + return() +endif() + +execute_process( + COMMAND xcrun -find otool + OUTPUT_VARIABLE clang_path + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_FILE /dev/null +) +string(REGEX MATCH "(.*/Toolchains)/.*" toolchains_match ${clang_path}) +if(NOT toolchains_match) + message(FATAL_ERROR "Could not identify toolchain dir") +endif() +set(toolchains_dir ${CMAKE_MATCH_1}) + +set(XcodeDefaultInfo "${toolchains_dir}/XcodeDefault.xctoolchain/ToolchainInfo.plist") +set(LLVMToolchainDir "${CMAKE_INSTALL_PREFIX}/Toolchains/LLVM${PACKAGE_VERSION}.xctoolchain/") + +add_custom_command(OUTPUT ${LLVMToolchainDir} + COMMAND ${CMAKE_COMMAND} -E make_directory ${LLVMToolchainDir}) + +add_custom_command(OUTPUT ${LLVMToolchainDir}/ToolchainInfo.plist + DEPENDS ${LLVMToolchainDir} + COMMAND ${CMAKE_COMMAND} -E copy "${XcodeDefaultInfo}" "${LLVMToolchainDir}/ToolchainInfo.plist" + COMMAND /usr/libexec/PlistBuddy -c "Set:Identifier org.llvm.${PACKAGE_VERSION}" "${LLVMToolchainDir}/ToolchainInfo.plist") + +add_custom_target(install-xcode-toolchain + DEPENDS ${LLVMToolchainDir}/ToolchainInfo.plist + COMMAND "${CMAKE_COMMAND}" --build ${CMAKE_BINARY_DIR} --target all + COMMAND "${CMAKE_COMMAND}" + -DCMAKE_INSTALL_PREFIX=${LLVMToolchainDir}/usr/ + -P "${CMAKE_BINARY_DIR}/cmake_install.cmake" + ${cmake_3_2_USES_TERMINAL}) diff --git a/tools/yaml2obj/yaml2elf.cpp b/tools/yaml2obj/yaml2elf.cpp index a247f48c053d..d03986e9c88d 100644 --- a/tools/yaml2obj/yaml2elf.cpp +++ b/tools/yaml2obj/yaml2elf.cpp @@ -105,10 +105,10 @@ class ELFState { typedef typename object::ELFFile<ELFT>::Elf_Rela Elf_Rela; /// \brief The future ".strtab" section. - StringTableBuilder DotStrtab; + StringTableBuilder DotStrtab{StringTableBuilder::ELF}; /// \brief The future ".shstrtab" section. - StringTableBuilder DotShStrtab; + StringTableBuilder DotShStrtab{StringTableBuilder::ELF}; NameToIdxMap SN2I; NameToIdxMap SymN2I; @@ -193,7 +193,7 @@ bool ELFState<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders, for (const auto &Sec : Doc.Sections) DotShStrtab.add(Sec->Name); - DotShStrtab.finalize(StringTableBuilder::ELF); + DotShStrtab.finalize(); for (const auto &Sec : Doc.Sections) { zero(SHeader); @@ -222,9 +222,11 @@ bool ELFState<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders, unsigned Index; if (SN2I.lookup(S->Info, Index)) { - errs() << "error: Unknown section referenced: '" << S->Info - << "' at YAML section '" << S->Name << "'.\n"; - return false; + if (S->Info.getAsInteger(0, Index)) { + errs() << "error: Unknown section referenced: '" << S->Info + << "' at YAML section '" << S->Name << "'.\n"; + return false; + } } SHeader.sh_info = Index; @@ -284,7 +286,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(StringTableBuilder::ELF); + DotStrtab.finalize(); addSymbols(Doc.Symbols.Local, Syms, ELF::STB_LOCAL); addSymbols(Doc.Symbols.Global, Syms, ELF::STB_GLOBAL); |
