diff options
Diffstat (limited to 'tools')
141 files changed, 11092 insertions, 1868 deletions
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 7e9938119f5d..7859b49c9de7 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -37,7 +37,7 @@ 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-vtabledump) +add_llvm_tool_subdirectory(llvm-cxxdump) if( LLVM_USE_INTEL_JITEVENTS ) add_llvm_tool_subdirectory(llvm-jitlistener) else() @@ -61,6 +61,8 @@ add_llvm_tool_subdirectory(yaml2obj) add_llvm_tool_subdirectory(llvm-go) +add_llvm_tool_subdirectory(llvm-pdbdump) + if(NOT CYGWIN AND LLVM_ENABLE_PIC) add_llvm_tool_subdirectory(lto) add_llvm_tool_subdirectory(llvm-lto) diff --git a/tools/LLVMBuild.txt b/tools/LLVMBuild.txt index 53d4ffc28dc1..6a838b72ecff 100644 --- a/tools/LLVMBuild.txt +++ b/tools/LLVMBuild.txt @@ -16,7 +16,10 @@ ;===------------------------------------------------------------------------===; [common] -subdirectories = bugpoint llc lli llvm-ar llvm-as llvm-bcanalyzer llvm-cov llvm-diff llvm-dis llvm-dwarfdump llvm-extract llvm-jitlistener llvm-link llvm-lto llvm-mc llvm-nm llvm-objdump llvm-profdata llvm-rtdyld llvm-size macho-dump opt llvm-mcmarkup verify-uselistorder dsymutil +subdirectories = bugpoint llc lli llvm-ar llvm-as llvm-bcanalyzer llvm-cov + llvm-diff llvm-dis llvm-dwarfdump llvm-extract llvm-jitlistener llvm-link + llvm-lto llvm-mc llvm-nm llvm-objdump llvm-pdbdump llvm-profdata llvm-rtdyld + llvm-size macho-dump opt llvm-mcmarkup verify-uselistorder dsymutil [component_0] type = Group diff --git a/tools/Makefile b/tools/Makefile index b559edacd52d..d8534866c3d3 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -33,7 +33,7 @@ PARALLEL_DIRS := opt llvm-as llvm-dis llc llvm-ar llvm-nm llvm-link \ macho-dump llvm-objdump llvm-readobj llvm-rtdyld \ llvm-dwarfdump llvm-cov llvm-size llvm-stress llvm-mcmarkup \ llvm-profdata llvm-symbolizer obj2yaml yaml2obj llvm-c-test \ - llvm-vtabledump verify-uselistorder dsymutil + llvm-cxxdump verify-uselistorder dsymutil llvm-pdbdump # 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 ed54e9f8dfc7..6979d0345ee6 100644 --- a/tools/bugpoint-passes/TestPasses.cpp +++ b/tools/bugpoint-passes/TestPasses.cpp @@ -68,8 +68,32 @@ namespace { } }; } - + char DeleteCalls::ID = 0; static RegisterPass<DeleteCalls> Y("bugpoint-deletecalls", "BugPoint Test Pass - Intentionally 'misoptimize' CallInsts"); + +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; + } + }; +} + +char CrashOnDeclFunc::ID = 0; +static RegisterPass<CrashOnDeclFunc> + Z("bugpoint-crash-decl-funcs", + "BugPoint Test Pass - Intentionally crash on declared functions"); diff --git a/tools/bugpoint/BugDriver.cpp b/tools/bugpoint/BugDriver.cpp index b8be17e44dd2..43f4c2963fc3 100644 --- a/tools/bugpoint/BugDriver.cpp +++ b/tools/bugpoint/BugDriver.cpp @@ -16,6 +16,7 @@ #include "BugDriver.h" #include "ToolRunner.h" #include "llvm/IR/Module.h" +#include "llvm/IR/Verifier.h" #include "llvm/IRReader/IRReader.h" #include "llvm/Linker/Linker.h" #include "llvm/Pass.h" @@ -86,23 +87,28 @@ std::unique_ptr<Module> llvm::parseInputFile(StringRef Filename, LLVMContext &Ctxt) { SMDiagnostic Err; std::unique_ptr<Module> Result = parseIRFile(Filename, Err, Ctxt); - if (!Result) + if (!Result) { Err.print("bugpoint", errs()); + return Result; + } + + if (verifyModule(*Result, &errs())) { + errs() << "bugpoint: " << Filename << ": error: input module is broken!\n"; + return std::unique_ptr<Module>(); + } // If we don't have an override triple, use the first one to configure // bugpoint, or use the host triple if none provided. - if (Result) { - if (TargetTriple.getTriple().empty()) { - Triple TheTriple(Result->getTargetTriple()); + if (TargetTriple.getTriple().empty()) { + Triple TheTriple(Result->getTargetTriple()); - if (TheTriple.getTriple().empty()) - TheTriple.setTriple(sys::getDefaultTargetTriple()); + if (TheTriple.getTriple().empty()) + TheTriple.setTriple(sys::getDefaultTargetTriple()); - TargetTriple.setTriple(TheTriple.getTriple()); - } - - Result->setTargetTriple(TargetTriple.getTriple()); // override the triple + TargetTriple.setTriple(TheTriple.getTriple()); } + + Result->setTargetTriple(TargetTriple.getTriple()); // override the triple return Result; } diff --git a/tools/bugpoint/CMakeLists.txt b/tools/bugpoint/CMakeLists.txt index d71e097918c4..daf502e16ccd 100644 --- a/tools/bugpoint/CMakeLists.txt +++ b/tools/bugpoint/CMakeLists.txt @@ -31,7 +31,7 @@ add_llvm_tool(bugpoint ToolRunner.cpp bugpoint.cpp ) -set_target_properties(bugpoint PROPERTIES ENABLE_EXPORTS 1) +export_executable_symbols(bugpoint) if(WITH_POLLY AND LINK_POLLY_INTO_TOOLS) target_link_libraries(bugpoint Polly) diff --git a/tools/bugpoint/CrashDebugger.cpp b/tools/bugpoint/CrashDebugger.cpp index bac948aaf3ab..6f41d3030d00 100644 --- a/tools/bugpoint/CrashDebugger.cpp +++ b/tools/bugpoint/CrashDebugger.cpp @@ -19,11 +19,11 @@ #include "llvm/IR/Constants.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Instructions.h" +#include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" #include "llvm/IR/ValueSymbolTable.h" #include "llvm/IR/Verifier.h" #include "llvm/Pass.h" -#include "llvm/PassManager.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileUtilities.h" #include "llvm/Transforms/Scalar.h" @@ -40,6 +40,15 @@ namespace { NoGlobalRM ("disable-global-remove", cl::desc("Do not remove global variables"), cl::init(false)); + + cl::opt<bool> + ReplaceFuncsWithNull("replace-funcs-with-null", + cl::desc("When stubbing functions, replace all uses will null"), + cl::init(false)); + cl::opt<bool> + DontReducePassList("disable-pass-list-reduction", + cl::desc("Skip pass list reduction steps"), + cl::init(false)); } namespace llvm { @@ -194,6 +203,29 @@ namespace { }; } +static void RemoveFunctionReferences(Module *M, const char* Name) { + auto *UsedVar = M->getGlobalVariable(Name, true); + if (!UsedVar || !UsedVar->hasInitializer()) return; + if (isa<ConstantAggregateZero>(UsedVar->getInitializer())) { + assert(UsedVar->use_empty()); + UsedVar->eraseFromParent(); + return; + } + auto *OldUsedVal = cast<ConstantArray>(UsedVar->getInitializer()); + std::vector<Constant*> Used; + for(Value *V : OldUsedVal->operand_values()) { + Constant *Op = cast<Constant>(V->stripPointerCasts()); + if(!Op->isNullValue()) { + Used.push_back(cast<Constant>(V)); + } + } + auto *NewValElemTy = OldUsedVal->getType()->getElementType(); + auto *NewValTy = ArrayType::get(NewValElemTy, Used.size()); + auto *NewUsedVal = ConstantArray::get(NewValTy, Used); + UsedVar->mutateType(NewUsedVal->getType()->getPointerTo()); + UsedVar->setInitializer(NewUsedVal); +} + bool ReduceCrashingFunctions::TestFuncs(std::vector<Function*> &Funcs) { // If main isn't present, claim there is no problem. if (KeepMain && std::find(Funcs.begin(), Funcs.end(), @@ -218,13 +250,53 @@ bool ReduceCrashingFunctions::TestFuncs(std::vector<Function*> &Funcs) { outs() << "Checking for crash with only these functions: "; PrintFunctionList(Funcs); outs() << ": "; + 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); + } else { + std::vector<GlobalValue*> ToRemove; + // First, remove aliases to functions we're about to purge. + for (GlobalAlias &Alias : M->aliases()) { + Constant *Root = Alias.getAliasee()->stripPointerCasts(); + Function *F = dyn_cast<Function>(Root); + if (F) { + if (Functions.count(F)) + // We're keeping this function. + continue; + } else if (Root->isNullValue()) { + // This referenced a globalalias that we've already replaced, + // so we still need to replace this alias. + } else if (!F) { + // Not a function, therefore not something we mess with. + continue; + } - // 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); + PointerType *Ty = cast<PointerType>(Alias.getType()); + Constant *Replacement = ConstantPointerNull::get(Ty); + Alias.replaceAllUsesWith(Replacement); + 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()); + Constant *Replacement = ConstantPointerNull::get(Ty); + I->replaceAllUsesWith(Replacement); + ToRemove.push_back(I); + } + } + for (auto *F : ToRemove) { + F->eraseFromParent(); + } + + // Finally, remove any null members from any global intrinsic. + RemoveFunctionReferences(M, "llvm.used"); + RemoveFunctionReferences(M, "llvm.compiler.used"); + } // Try running the hacked up program... if (TestFn(BD, M)) { BD.setNewProgram(M); // It crashed, keep the trimmed version... @@ -296,7 +368,7 @@ bool ReduceCrashingBlocks::TestBlocks(std::vector<const BasicBlock*> &BBs) { (*SI)->removePredecessor(BB); TerminatorInst *BBTerm = BB->getTerminator(); - + if (!BB->getTerminator()->getType()->isVoidTy()) BBTerm->replaceAllUsesWith(Constant::getNullValue(BBTerm->getType())); @@ -407,9 +479,8 @@ bool ReduceCrashingInstructions::TestInsts(std::vector<const Instruction*> } // Verify that this is still valid. - PassManager Passes; + legacy::PassManager Passes; Passes.add(createVerifierPass()); - Passes.add(createDebugInfoVerifierPass()); Passes.run(*M); // Try running on the hacked up program... @@ -630,7 +701,7 @@ bool BugDriver::debugOptimizerCrash(const std::string &ID) { std::string Error; // Reduce the list of passes which causes the optimizer to crash... - if (!BugpointIsInterrupted) + if (!BugpointIsInterrupted && !DontReducePassList) ReducePassList(*this).reduceList(PassesToRun, Error); assert(Error.empty()); diff --git a/tools/bugpoint/ExtractFunction.cpp b/tools/bugpoint/ExtractFunction.cpp index 34fe53c08112..238cbbc70a03 100644 --- a/tools/bugpoint/ExtractFunction.cpp +++ b/tools/bugpoint/ExtractFunction.cpp @@ -17,10 +17,10 @@ #include "llvm/IR/DataLayout.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/LLVMContext.h" +#include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" #include "llvm/IR/Verifier.h" #include "llvm/Pass.h" -#include "llvm/PassManager.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/FileUtilities.h" @@ -195,9 +195,9 @@ static Constant *GetTorInit(std::vector<std::pair<Function*, int> > &TorList) { assert(!TorList.empty() && "Don't create empty tor list!"); std::vector<Constant*> ArrayElts; Type *Int32Ty = Type::getInt32Ty(TorList[0].first->getContext()); - + StructType *STy = - StructType::get(Int32Ty, TorList[0].first->getType(), NULL); + StructType::get(Int32Ty, TorList[0].first->getType(), nullptr); for (unsigned i = 0, e = TorList.size(); i != e; ++i) { Constant *Elts[] = { ConstantInt::get(Int32Ty, TorList[i].second), diff --git a/tools/bugpoint/Miscompilation.cpp b/tools/bugpoint/Miscompilation.cpp index 8cb45838773a..53631d25c390 100644 --- a/tools/bugpoint/Miscompilation.cpp +++ b/tools/bugpoint/Miscompilation.cpp @@ -852,7 +852,8 @@ static void CleanupAndPrepareModules(BugDriver &BD, Module *&Test, // GetElementPtr *funcName, ulong 0, ulong 0 std::vector<Constant*> GEPargs(2, Constant::getNullValue(Type::getInt32Ty(F->getContext()))); - Value *GEP = ConstantExpr::getGetElementPtr(funcName, GEPargs); + Value *GEP = ConstantExpr::getGetElementPtr(InitArray->getType(), + funcName, GEPargs); std::vector<Value*> ResolverArgs; ResolverArgs.push_back(GEP); @@ -975,7 +976,7 @@ static bool TestCodeGenerator(BugDriver &BD, Module *Test, Module *Safe, } if (BD.writeProgramToFile(SafeModuleBC.str(), SafeModuleFD, Safe)) { - errs() << "Error writing bitcode to `" << SafeModuleBC.str() + errs() << "Error writing bitcode to `" << SafeModuleBC << "'\nExiting."; exit(1); } @@ -1050,7 +1051,7 @@ bool BugDriver::debugCodeGenerator(std::string *Error) { } if (writeProgramToFile(TestModuleBC.str(), TestModuleFD, ToCodeGen)) { - errs() << "Error writing bitcode to `" << TestModuleBC.str() + errs() << "Error writing bitcode to `" << TestModuleBC << "'\nExiting."; exit(1); } @@ -1068,7 +1069,7 @@ bool BugDriver::debugCodeGenerator(std::string *Error) { } if (writeProgramToFile(SafeModuleBC.str(), SafeModuleFD, ToNotCodeGen)) { - errs() << "Error writing bitcode to `" << SafeModuleBC.str() + errs() << "Error writing bitcode to `" << SafeModuleBC << "'\nExiting."; exit(1); } @@ -1079,17 +1080,17 @@ bool BugDriver::debugCodeGenerator(std::string *Error) { outs() << "You can reproduce the problem with the command line: \n"; if (isExecutingJIT()) { - outs() << " lli -load " << SharedObject << " " << TestModuleBC.str(); + outs() << " lli -load " << SharedObject << " " << TestModuleBC; } else { - outs() << " llc " << TestModuleBC.str() << " -o " << TestModuleBC.str() + outs() << " llc " << TestModuleBC << " -o " << TestModuleBC << ".s\n"; outs() << " gcc " << SharedObject << " " << TestModuleBC.str() - << ".s -o " << TestModuleBC.str() << ".exe"; + << ".s -o " << TestModuleBC << ".exe"; #if defined (HAVE_LINK_R) outs() << " -Wl,-R."; #endif outs() << "\n"; - outs() << " " << TestModuleBC.str() << ".exe"; + outs() << " " << TestModuleBC << ".exe"; } for (unsigned i = 0, e = InputArgv.size(); i != e; ++i) outs() << " " << InputArgv[i]; diff --git a/tools/bugpoint/OptimizerDriver.cpp b/tools/bugpoint/OptimizerDriver.cpp index f197cc53926a..344e7b588fb8 100644 --- a/tools/bugpoint/OptimizerDriver.cpp +++ b/tools/bugpoint/OptimizerDriver.cpp @@ -18,9 +18,9 @@ #include "BugDriver.h" #include "llvm/Bitcode/ReaderWriter.h" #include "llvm/IR/DataLayout.h" +#include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" #include "llvm/IR/Verifier.h" -#include "llvm/PassManager.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/FileUtilities.h" @@ -42,6 +42,11 @@ namespace llvm { extern cl::opt<std::string> OutputPrefix; } +static cl::opt<bool> PreserveBitcodeUseListOrder( + "preserve-bc-uselistorder", + cl::desc("Preserve use-list order when writing LLVM bitcode."), + cl::init(true), cl::Hidden); + namespace { // ChildOutput - This option captures the name of the child output file that // is set up by the parent bugpoint process @@ -55,7 +60,7 @@ namespace { /// file. If an error occurs, true is returned. /// static bool writeProgramToFileAux(tool_output_file &Out, const Module *M) { - WriteBitcodeToFile(M, Out.os()); + WriteBitcodeToFile(M, Out.os(), PreserveBitcodeUseListOrder); Out.os().close(); if (!Out.os().has_error()) { Out.keep(); @@ -151,7 +156,7 @@ bool BugDriver::runPasses(Module *Program, tool_output_file InFile(InputFilename, InputFD); - WriteBitcodeToFile(Program, InFile.os()); + WriteBitcodeToFile(Program, InFile.os(), PreserveBitcodeUseListOrder); InFile.os().close(); if (InFile.os().has_error()) { errs() << "Error writing bitcode file: " << InputFilename << "\n"; diff --git a/tools/bugpoint/ToolRunner.h b/tools/bugpoint/ToolRunner.h index 454724ace5ce..5d67a9426870 100644 --- a/tools/bugpoint/ToolRunner.h +++ b/tools/bugpoint/ToolRunner.h @@ -165,7 +165,7 @@ public: ToolArgs.clear(); if (Args) ToolArgs = *Args; } - ~LLC() { delete gcc; } + ~LLC() override { delete gcc; } /// compileProgram - Compile the specified program from bitcode to executable /// code. This does not produce any output, it is only used when debugging diff --git a/tools/bugpoint/bugpoint.cpp b/tools/bugpoint/bugpoint.cpp index d0bade507d2a..af6d9fccf291 100644 --- a/tools/bugpoint/bugpoint.cpp +++ b/tools/bugpoint/bugpoint.cpp @@ -16,10 +16,10 @@ #include "BugDriver.h" #include "ToolRunner.h" #include "llvm/IR/LLVMContext.h" +#include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/LegacyPassNameParser.h" #include "llvm/LinkAllIR.h" #include "llvm/LinkAllPasses.h" -#include "llvm/PassManager.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/PluginLoader.h" @@ -50,7 +50,7 @@ TimeoutValue("timeout", cl::init(300), cl::value_desc("seconds"), static cl::opt<int> MemoryLimit("mlimit", cl::init(-1), cl::value_desc("MBytes"), cl::desc("Maximum amount of memory to use. 0 disables check." - " Defaults to 300MB (800MB under valgrind).")); + " Defaults to 400MB (800MB under valgrind).")); static cl::opt<bool> UseValgrind("enable-valgrind", @@ -92,7 +92,7 @@ static void BugpointInterruptFunction() { // Hack to capture a pass list. namespace { - class AddToDriver : public FunctionPassManager { + class AddToDriver : public legacy::FunctionPassManager { BugDriver &D; public: AddToDriver(BugDriver &_D) : FunctionPassManager(nullptr), D(_D) {} @@ -158,7 +158,7 @@ int main(int argc, char **argv) { if (sys::RunningOnValgrind() || UseValgrind) MemoryLimit = 800; else - MemoryLimit = 300; + MemoryLimit = 400; } BugDriver D(argv[0], FindBugs, TimeoutValue, MemoryLimit, diff --git a/tools/dsymutil/CMakeLists.txt b/tools/dsymutil/CMakeLists.txt index bfe7c706391a..59b37a9b2900 100644 --- a/tools/dsymutil/CMakeLists.txt +++ b/tools/dsymutil/CMakeLists.txt @@ -1,4 +1,8 @@ set(LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + AsmPrinter + DebugInfoDWARF + MC Object Support ) diff --git a/tools/dsymutil/DebugMap.cpp b/tools/dsymutil/DebugMap.cpp index 7898160ae6b9..9fa3f788a89f 100644 --- a/tools/dsymutil/DebugMap.cpp +++ b/tools/dsymutil/DebugMap.cpp @@ -23,9 +23,12 @@ DebugMapObject::DebugMapObject(StringRef ObjectFilename) : Filename(ObjectFilename) {} bool DebugMapObject::addSymbol(StringRef Name, uint64_t ObjectAddress, - uint64_t LinkedAddress) { + uint64_t LinkedAddress, uint32_t Size) { auto InsertResult = Symbols.insert( - std::make_pair(Name, SymbolMapping(ObjectAddress, LinkedAddress))); + std::make_pair(Name, SymbolMapping(ObjectAddress, LinkedAddress, Size))); + + if (InsertResult.second) + AddressToMapping[ObjectAddress] = &*InsertResult.first; return InsertResult.second; } @@ -42,9 +45,9 @@ void DebugMapObject::print(raw_ostream &OS) const { Entries.begin(), Entries.end(), [](const Entry &LHS, const Entry &RHS) { return LHS.first < RHS.first; }); for (const auto &Sym : Entries) { - OS << format("\t%016" PRIx64 " => %016" PRIx64 "\t%s\n", + OS << format("\t%016" PRIx64 " => %016" PRIx64 "+0x%x\t%s\n", Sym.second.ObjectAddress, Sym.second.BinaryAddress, - Sym.first.data()); + Sym.second.Size, Sym.first.data()); } OS << '\n'; } @@ -58,16 +61,25 @@ DebugMapObject &DebugMap::addDebugMapObject(StringRef ObjectFilePath) { return *Objects.back(); } -const DebugMapObject::SymbolMapping * +const DebugMapObject::DebugMapEntry * DebugMapObject::lookupSymbol(StringRef SymbolName) const { StringMap<SymbolMapping>::const_iterator Sym = Symbols.find(SymbolName); if (Sym == Symbols.end()) return nullptr; - return &Sym->getValue(); + return &*Sym; +} + +const DebugMapObject::DebugMapEntry * +DebugMapObject::lookupObjectAddress(uint64_t Address) const { + auto Mapping = AddressToMapping.find(Address); + if (Mapping == AddressToMapping.end()) + return nullptr; + return Mapping->getSecond(); } void DebugMap::print(raw_ostream &OS) const { - OS << "DEBUG MAP: object addr => executable addr\tsymbol name\n"; + OS << "DEBUG MAP: " << BinaryTriple.getTriple() + << "\n\tobject addr => executable addr\tsymbol name\n"; for (const auto &Obj : objects()) Obj->print(OS); OS << "END DEBUG MAP\n"; diff --git a/tools/dsymutil/DebugMap.h b/tools/dsymutil/DebugMap.h index 54bff3272080..ee48b093d4fd 100644 --- a/tools/dsymutil/DebugMap.h +++ b/tools/dsymutil/DebugMap.h @@ -21,7 +21,9 @@ #ifndef LLVM_TOOLS_DSYMUTIL_DEBUGMAP_H #define LLVM_TOOLS_DSYMUTIL_DEBUGMAP_H +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringMap.h" +#include "llvm/ADT/Triple.h" #include "llvm/ADT/iterator_range.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/ErrorOr.h" @@ -60,10 +62,13 @@ class DebugMapObject; /// } /// } class DebugMap { + Triple BinaryTriple; typedef std::vector<std::unique_ptr<DebugMapObject>> ObjectContainer; ObjectContainer Objects; public: + DebugMap(const Triple &BinaryTriple) : BinaryTriple(BinaryTriple) {} + typedef ObjectContainer::const_iterator const_iterator; iterator_range<const_iterator> objects() const { @@ -78,6 +83,8 @@ public: /// debug map. DebugMapObject &addDebugMapObject(StringRef ObjectFilePath); + const Triple &getTriple() const { return BinaryTriple; } + void print(raw_ostream &OS) const; #ifndef NDEBUG @@ -94,22 +101,34 @@ public: struct SymbolMapping { uint64_t ObjectAddress; uint64_t BinaryAddress; - SymbolMapping(uint64_t ObjectAddress, uint64_t BinaryAddress) - : ObjectAddress(ObjectAddress), BinaryAddress(BinaryAddress) {} + uint32_t Size; + SymbolMapping(uint64_t ObjectAddress, uint64_t BinaryAddress, uint32_t Size) + : ObjectAddress(ObjectAddress), BinaryAddress(BinaryAddress), + Size(Size) {} }; + typedef StringMapEntry<SymbolMapping> DebugMapEntry; + /// \brief Adds a symbol mapping to this DebugMapObject. /// \returns false if the symbol was already registered. The request /// is discarded in this case. bool addSymbol(llvm::StringRef SymName, uint64_t ObjectAddress, - uint64_t LinkedAddress); + uint64_t LinkedAddress, uint32_t Size); /// \brief Lookup a symbol mapping. /// \returns null if the symbol isn't found. - const SymbolMapping *lookupSymbol(StringRef SymbolName) const; + const DebugMapEntry *lookupSymbol(StringRef SymbolName) const; + + /// \brief Lookup an objectfile address. + /// \returns null if the address isn't found. + const DebugMapEntry *lookupObjectAddress(uint64_t Address) const; llvm::StringRef getObjectFilename() const { return Filename; } + iterator_range<StringMap<SymbolMapping>::const_iterator> symbols() const { + return make_range(Symbols.begin(), Symbols.end()); + } + void print(raw_ostream &OS) const; #ifndef NDEBUG void dump() const; @@ -121,6 +140,7 @@ private: std::string Filename; StringMap<SymbolMapping> Symbols; + DenseMap<uint64_t, DebugMapEntry *> AddressToMapping; }; } } diff --git a/tools/dsymutil/DwarfLinker.cpp b/tools/dsymutil/DwarfLinker.cpp index ad471055bd9c..a6e62a838945 100644 --- a/tools/dsymutil/DwarfLinker.cpp +++ b/tools/dsymutil/DwarfLinker.cpp @@ -7,14 +7,2536 @@ // //===----------------------------------------------------------------------===// #include "DebugMap.h" +#include "BinaryHolder.h" +#include "DebugMap.h" #include "dsymutil.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/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h" +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCDwarf.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Object/MachO.h" +#include "llvm/Support/Dwarf.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetOptions.h" +#include <string> +#include <tuple> namespace llvm { namespace dsymutil { -bool linkDwarf(StringRef OutputFilename, const DebugMap &DM, bool Verbose) { - // Do nothing for now. +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, + IntervalMapHalfOpenInfo<KeyT>>; + +typedef HalfOpenIntervalMap<uint64_t, int64_t> FunctionIntervals; + +/// \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. +class CompileUnit { +public: + /// \brief Information gathered about a DIE in the object file. + struct DIEInfo { + int64_t AddrAdjust; ///< Address offset to apply to the described entity. + 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? + }; + + CompileUnit(DWARFUnit &OrigUnit, unsigned ID) + : OrigUnit(OrigUnit), ID(ID), LowPc(UINT64_MAX), HighPc(0), RangeAlloc(), + Ranges(RangeAlloc), UnitRangeAttribute(nullptr) { + Info.resize(OrigUnit.getNumDIEs()); + } + + CompileUnit(CompileUnit &&RHS) + : OrigUnit(RHS.OrigUnit), Info(std::move(RHS.Info)), + CUDie(std::move(RHS.CUDie)), StartOffset(RHS.StartOffset), + NextUnitOffset(RHS.NextUnitOffset), RangeAlloc(), Ranges(RangeAlloc) { + // The CompileUnit container has been 'reserve()'d with the right + // size. We cannot move the IntervalMap anyway. + llvm_unreachable("CompileUnits should not be moved."); + } + + DWARFUnit &getOrigUnit() const { return OrigUnit; } + + unsigned getUniqueID() const { return ID; } + + DIE *getOutputUnitDIE() const { return CUDie.get(); } + void setOutputUnitDIE(DIE *Die) { CUDie.reset(Die); } + + DIEInfo &getInfo(unsigned Idx) { return Info[Idx]; } + const DIEInfo &getInfo(unsigned Idx) const { return Info[Idx]; } + + uint64_t getStartOffset() const { return StartOffset; } + uint64_t getNextUnitOffset() const { return NextUnitOffset; } + void setStartOffset(uint64_t DebugInfoSize) { StartOffset = DebugInfoSize; } + + uint64_t getLowPc() const { return LowPc; } + uint64_t getHighPc() const { return HighPc; } + + DIEInteger *getUnitRangesAttribute() const { return UnitRangeAttribute; } + const FunctionIntervals &getFunctionRanges() const { return Ranges; } + const std::vector<DIEInteger *> &getRangesAttributes() const { + return RangeAttributes; + } + + const std::vector<std::pair<DIEInteger *, int64_t>> & + getLocationAttributes() const { + return LocationAttributes; + } + + /// \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 + /// debug_info section size). + uint64_t computeNextUnitOffset(); + + /// \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. + void noteForwardReference(DIE *Die, const CompileUnit *RefUnit, + DIEInteger *Attr); + + /// \brief Apply all fixups recored by noteForwardReference(). + void fixupForwardReferences(); + + /// \brief Add a function range [\p LowPC, \p HighPC) that is + /// relocatad by applying offset \p PCOffset. + void addFunctionRange(uint64_t LowPC, uint64_t HighPC, int64_t PCOffset); + + /// \brief Keep track of a DW_AT_range attribute that we will need to + /// patch up later. + void noteRangeAttribute(const DIE &Die, DIEInteger *Attr); + + /// \brief Keep track of a location attribute pointing to a location + /// list in the debug_loc section. + void noteLocationAttribute(DIEInteger *Attr, int64_t PcOffset); + + /// \brief Add a name accelerator entry for \p Die with \p Name + /// which is stored in the string table at \p Offset. + void addNameAccelerator(const DIE *Die, const char *Name, uint32_t Offset, + bool SkipPubnamesSection = false); + + /// \brief Add a type accelerator entry for \p Die with \p Name + /// which is stored in the string table at \p Offset. + void addTypeAccelerator(const DIE *Die, const char *Name, uint32_t Offset); + + struct AccelInfo { + StringRef Name; ///< Name of the entry. + const DIE *Die; ///< DIE this entry describes. + uint32_t NameOffset; ///< Offset of Name in the string pool. + bool SkipPubSection; ///< Emit this entry only in the apple_* sections. + + AccelInfo(StringRef Name, const DIE *Die, uint32_t NameOffset, + bool SkipPubSection = false) + : Name(Name), Die(Die), NameOffset(NameOffset), + SkipPubSection(SkipPubSection) {} + }; + + const std::vector<AccelInfo> &getPubnames() const { return Pubnames; } + const std::vector<AccelInfo> &getPubtypes() const { return Pubtypes; } + +private: + DWARFUnit &OrigUnit; + unsigned ID; + std::vector<DIEInfo> Info; ///< DIE info indexed by DIE index. + std::unique_ptr<DIE> CUDie; ///< Root of the linked DIE tree. + + uint64_t StartOffset; + uint64_t NextUnitOffset; + + uint64_t LowPc; + uint64_t HighPc; + + /// \brief A list of attributes to fixup with the absolute offset of + /// a DIE in the debug_info section. + /// + /// 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 *, DIEInteger *>> + ForwardDIEReferences; + + FunctionIntervals::Allocator RangeAlloc; + /// \brief The ranges in that interval map are the PC ranges for + /// functions in this unit, associated with the PC offset to apply + /// to the addresses to get the linked address. + FunctionIntervals Ranges; + + /// \brief DW_AT_ranges attributes to patch after we have gathered + /// all the unit's function addresses. + /// @{ + std::vector<DIEInteger *> RangeAttributes; + DIEInteger *UnitRangeAttribute; + /// @} + + /// \brief Location attributes that need to be transfered from th + /// original debug_loc section to the liked one. They are stored + /// along with the PC offset that is to be applied to their + /// function's address. + std::vector<std::pair<DIEInteger *, int64_t>> LocationAttributes; + + /// \brief Accelerator entries for the unit, both for the pub* + /// sections and the apple* ones. + /// @{ + std::vector<AccelInfo> Pubnames; + std::vector<AccelInfo> Pubtypes; + /// @} +}; + +uint64_t CompileUnit::computeNextUnitOffset() { + NextUnitOffset = StartOffset + 11 /* Header size */; + // The root DIE might be null, meaning that the Unit had nothing to + // contribute to the linked output. In that case, we will emit the + // unit header without any actual DIE. + if (CUDie) + NextUnitOffset += CUDie->getSize(); + return NextUnitOffset; +} + +/// \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, + DIEInteger *Attr) { + ForwardDIEReferences.emplace_back(Die, RefUnit, Attr); +} + +/// \brief Apply all fixups recorded by noteForwardReference(). +void CompileUnit::fixupForwardReferences() { + for (const auto &Ref : ForwardDIEReferences) { + DIE *RefDie; + const CompileUnit *RefUnit; + DIEInteger *Attr; + std::tie(RefDie, RefUnit, Attr) = Ref; + Attr->setValue(RefDie->getOffset() + RefUnit->getStartOffset()); + } +} + +void CompileUnit::addFunctionRange(uint64_t FuncLowPc, uint64_t FuncHighPc, + int64_t PcOffset) { + Ranges.insert(FuncLowPc, FuncHighPc, PcOffset); + this->LowPc = std::min(LowPc, FuncLowPc + PcOffset); + this->HighPc = std::max(HighPc, FuncHighPc + PcOffset); +} + +void CompileUnit::noteRangeAttribute(const DIE &Die, DIEInteger *Attr) { + if (Die.getTag() != dwarf::DW_TAG_compile_unit) + RangeAttributes.push_back(Attr); + else + UnitRangeAttribute = Attr; +} + +void CompileUnit::noteLocationAttribute(DIEInteger *Attr, int64_t PcOffset) { + LocationAttributes.emplace_back(Attr, PcOffset); +} + +/// \brief Add a name accelerator entry for \p Die with \p Name +/// which is stored in the string table at \p Offset. +void CompileUnit::addNameAccelerator(const DIE *Die, const char *Name, + uint32_t Offset, bool SkipPubSection) { + Pubnames.emplace_back(Name, Die, Offset, SkipPubSection); +} + +/// \brief Add a type accelerator entry for \p Die with \p Name +/// which is stored in the string table at \p Offset. +void CompileUnit::addTypeAccelerator(const DIE *Die, const char *Name, + uint32_t Offset) { + 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 +/// information binary representation are handled in this class. +class DwarfStreamer { + /// \defgroup MCObjects MC layer objects constructed by the streamer + /// @{ + std::unique_ptr<MCRegisterInfo> MRI; + std::unique_ptr<MCAsmInfo> MAI; + std::unique_ptr<MCObjectFileInfo> MOFI; + std::unique_ptr<MCContext> MC; + MCAsmBackend *MAB; // Owned by MCStreamer + std::unique_ptr<MCInstrInfo> MII; + std::unique_ptr<MCSubtargetInfo> MSTI; + MCCodeEmitter *MCE; // Owned by MCStreamer + MCStreamer *MS; // Owned by AsmPrinter + std::unique_ptr<TargetMachine> TM; + std::unique_ptr<AsmPrinter> Asm; + /// @} + + /// \brief the file we stream the linked Dwarf to. + std::unique_ptr<raw_fd_ostream> OutFile; + + uint32_t RangesSectionSize; + uint32_t LocSectionSize; + uint32_t LineSectionSize; + + /// \brief Emit the pubnames or pubtypes section contribution for \p + /// Unit into \p Sec. The data is provided in \p Names. + void emitPubSectionForUnit(MCSection *Sec, StringRef Name, + const CompileUnit &Unit, + const std::vector<CompileUnit::AccelInfo> &Names); + +public: + /// \brief Actually create the streamer and the ouptut file. + /// + /// This could be done directly in the constructor, but it feels + /// more natural to handle errors through return value. + bool init(Triple TheTriple, StringRef OutputFilename); + + /// \brief Dump the file to the disk. + bool finish(); + + AsmPrinter &getAsmPrinter() const { return *Asm; } + + /// \brief Set the current output section to debug_info and change + /// the MC Dwarf version to \p DwarfVersion. + void switchToDebugInfoSection(unsigned DwarfVersion); + + /// \brief Emit the compilation unit header for \p Unit in the + /// debug_info section. + /// + /// As a side effect, this also switches the current Dwarf version + /// of the MC layer to the one of U.getOrigUnit(). + void emitCompileUnitHeader(CompileUnit &Unit); + + /// \brief Recursively emit the DIE tree rooted at \p Die. + void emitDIE(DIE &Die); + + /// \brief Emit the abbreviation table \p Abbrevs to the + /// debug_abbrev section. + void emitAbbrevs(const std::vector<DIEAbbrev *> &Abbrevs); + + /// \brief Emit the string table described by \p Pool. + void emitStrings(const NonRelocatableStringpool &Pool); + + /// \brief Emit debug_ranges for \p FuncRange by translating the + /// original \p Entries. + void emitRangesEntries( + int64_t UnitPcOffset, uint64_t OrigLowPc, + FunctionIntervals::const_iterator FuncRange, + const std::vector<DWARFDebugRangeList::RangeListEntry> &Entries, + unsigned AddressSize); + + /// \brief Emit debug_aranges entries for \p Unit and if \p + /// DoRangesSection is true, also emit the debug_ranges entries for + /// the DW_TAG_compile_unit's DW_AT_ranges attribute. + void emitUnitRangesEntries(CompileUnit &Unit, bool DoRangesSection); + + uint32_t getRangesSectionSize() const { return RangesSectionSize; } + + /// \brief Emit the debug_loc contribution for \p Unit by copying + /// the entries from \p Dwarf and offseting them. Update the + /// location attributes to point to the new entries. + void emitLocationsForUnit(const CompileUnit &Unit, DWARFContext &Dwarf); + + /// \brief Emit the line table described in \p Rows into the + /// debug_line section. + void emitLineTableForUnit(StringRef PrologueBytes, unsigned MinInstLength, + std::vector<DWARFDebugLine::Row> &Rows, + unsigned AdddressSize); + + uint32_t getLineSectionSize() const { return LineSectionSize; } + + /// \brief Emit the .debug_pubnames contribution for \p Unit. + void emitPubNamesForUnit(const CompileUnit &Unit); + + /// \brief Emit the .debug_pubtypes contribution for \p Unit. + void emitPubTypesForUnit(const CompileUnit &Unit); +}; + +bool DwarfStreamer::init(Triple TheTriple, StringRef OutputFilename) { + std::string ErrorStr; + std::string TripleName; + StringRef Context = "dwarf streamer init"; + + // Get the target. + const Target *TheTarget = + TargetRegistry::lookupTarget(TripleName, TheTriple, ErrorStr); + if (!TheTarget) + return error(ErrorStr, Context); + TripleName = TheTriple.getTriple(); + + // Create all the MC Objects. + MRI.reset(TheTarget->createMCRegInfo(TripleName)); + if (!MRI) + return error(Twine("no register info for target ") + TripleName, Context); + + MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName)); + if (!MAI) + return error("no asm info for target " + TripleName, Context); + + MOFI.reset(new MCObjectFileInfo); + MC.reset(new MCContext(MAI.get(), MRI.get(), MOFI.get())); + MOFI->InitMCObjectFileInfo(TripleName, Reloc::Default, CodeModel::Default, + *MC); + + MAB = TheTarget->createMCAsmBackend(*MRI, TripleName, ""); + if (!MAB) + return error("no asm backend for target " + TripleName, Context); + + MII.reset(TheTarget->createMCInstrInfo()); + if (!MII) + return error("no instr info info for target " + TripleName, Context); + + MSTI.reset(TheTarget->createMCSubtargetInfo(TripleName, "", "")); + if (!MSTI) + return error("no subtarget info for target " + TripleName, Context); + + 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; + OutFile = + llvm::make_unique<raw_fd_ostream>(OutputFilename, EC, sys::fs::F_None); + if (EC) + return error(Twine(OutputFilename) + ": " + EC.message(), Context); + + MS = TheTarget->createMCObjectStreamer(TheTriple, *MC, *MAB, *OutFile, MCE, + *MSTI, false, + /*DWARFMustBeAtTheEnd*/ false); + if (!MS) + return error("no object streamer for target " + TripleName, Context); + + // Finally create the AsmPrinter we'll use to emit the DIEs. + TM.reset(TheTarget->createTargetMachine(TripleName, "", "", TargetOptions())); + if (!TM) + return error("no target machine for target " + TripleName, Context); + + Asm.reset(TheTarget->createAsmPrinter(*TM, std::unique_ptr<MCStreamer>(MS))); + if (!Asm) + return error("no asm printer for target " + TripleName, Context); + + RangesSectionSize = 0; + LocSectionSize = 0; + LineSectionSize = 0; + + return true; +} + +bool DwarfStreamer::finish() { + MS->Finish(); return true; } + +/// \brief Set the current output section to debug_info and change +/// the MC Dwarf version to \p DwarfVersion. +void DwarfStreamer::switchToDebugInfoSection(unsigned DwarfVersion) { + MS->SwitchSection(MOFI->getDwarfInfoSection()); + MC->setDwarfVersion(DwarfVersion); +} + +/// \brief Emit the compilation unit header for \p Unit in the +/// debug_info section. +/// +/// A Dwarf scetion header is encoded as: +/// uint32_t Unit length (omiting this field) +/// uint16_t Version +/// uint32_t Abbreviation table offset +/// uint8_t Address size +/// +/// Leading to a total of 11 bytes. +void DwarfStreamer::emitCompileUnitHeader(CompileUnit &Unit) { + unsigned Version = Unit.getOrigUnit().getVersion(); + switchToDebugInfoSection(Version); + + // Emit size of content not including length itself. The size has + // already been computed in CompileUnit::computeOffsets(). Substract + // 4 to that size to account for the length field. + Asm->EmitInt32(Unit.getNextUnitOffset() - Unit.getStartOffset() - 4); + Asm->EmitInt16(Version); + // We share one abbreviations table across all units so it's always at the + // start of the section. + Asm->EmitInt32(0); + Asm->EmitInt8(Unit.getOrigUnit().getAddressByteSize()); +} + +/// \brief Emit the \p Abbrevs array as the shared abbreviation table +/// for the linked Dwarf file. +void DwarfStreamer::emitAbbrevs(const std::vector<DIEAbbrev *> &Abbrevs) { + MS->SwitchSection(MOFI->getDwarfAbbrevSection()); + Asm->emitDwarfAbbrevs(Abbrevs); +} + +/// \brief Recursively emit the DIE tree rooted at \p Die. +void DwarfStreamer::emitDIE(DIE &Die) { + MS->SwitchSection(MOFI->getDwarfInfoSection()); + Asm->emitDwarfDIE(Die); +} + +/// \brief Emit the debug_str section stored in \p Pool. +void DwarfStreamer::emitStrings(const NonRelocatableStringpool &Pool) { + Asm->OutStreamer->SwitchSection(MOFI->getDwarfStrSection()); + for (auto *Entry = Pool.getFirstEntry(); Entry; + Entry = Pool.getNextEntry(Entry)) + Asm->OutStreamer->EmitBytes( + StringRef(Entry->getKey().data(), Entry->getKey().size() + 1)); +} + +/// \brief Emit the debug_range section contents for \p FuncRange by +/// translating the original \p Entries. The debug_range section +/// format is totally trivial, consisting just of pairs of address +/// sized addresses describing the ranges. +void DwarfStreamer::emitRangesEntries( + int64_t UnitPcOffset, uint64_t OrigLowPc, + FunctionIntervals::const_iterator FuncRange, + const std::vector<DWARFDebugRangeList::RangeListEntry> &Entries, + unsigned AddressSize) { + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfRangesSection()); + + // Offset each range by the right amount. + int64_t PcOffset = FuncRange.value() + UnitPcOffset; + for (const auto &Range : Entries) { + if (Range.isBaseAddressSelectionEntry(AddressSize)) { + warn("unsupported base address selection operation", + "emitting debug_ranges"); + break; + } + // Do not emit empty ranges. + if (Range.StartAddress == Range.EndAddress) + continue; + + // All range entries should lie in the function range. + if (!(Range.StartAddress + OrigLowPc >= FuncRange.start() && + Range.EndAddress + OrigLowPc <= FuncRange.stop())) + warn("inconsistent range data.", "emitting debug_ranges"); + MS->EmitIntValue(Range.StartAddress + PcOffset, AddressSize); + MS->EmitIntValue(Range.EndAddress + PcOffset, AddressSize); + RangesSectionSize += 2 * AddressSize; + } + + // Add the terminator entry. + MS->EmitIntValue(0, AddressSize); + MS->EmitIntValue(0, AddressSize); + RangesSectionSize += 2 * AddressSize; +} + +/// \brief Emit the debug_aranges contribution of a unit and +/// if \p DoDebugRanges is true the debug_range contents for a +/// compile_unit level DW_AT_ranges attribute (Which are basically the +/// same thing with a different base address). +/// Just aggregate all the ranges gathered inside that unit. +void DwarfStreamer::emitUnitRangesEntries(CompileUnit &Unit, + bool DoDebugRanges) { + unsigned AddressSize = Unit.getOrigUnit().getAddressByteSize(); + // Gather the ranges in a vector, so that we can simplify them. The + // IntervalMap will have coalesced the non-linked ranges, but here + // we want to coalesce the linked addresses. + std::vector<std::pair<uint64_t, uint64_t>> Ranges; + const auto &FunctionRanges = Unit.getFunctionRanges(); + for (auto Range = FunctionRanges.begin(), End = FunctionRanges.end(); + Range != End; ++Range) + Ranges.push_back(std::make_pair(Range.start() + Range.value(), + Range.stop() + Range.value())); + + // The object addresses where sorted, but again, the linked + // addresses might end up in a different order. + std::sort(Ranges.begin(), Ranges.end()); + + if (!Ranges.empty()) { + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfARangesSection()); + + MCSymbol *BeginLabel = Asm->createTempSymbol("Barange"); + MCSymbol *EndLabel = Asm->createTempSymbol("Earange"); + + unsigned HeaderSize = + sizeof(int32_t) + // Size of contents (w/o this field + sizeof(int16_t) + // DWARF ARange version number + sizeof(int32_t) + // Offset of CU in the .debug_info section + sizeof(int8_t) + // Pointer Size (in bytes) + sizeof(int8_t); // Segment Size (in bytes) + + unsigned TupleSize = AddressSize * 2; + unsigned Padding = OffsetToAlignment(HeaderSize, TupleSize); + + Asm->EmitLabelDifference(EndLabel, BeginLabel, 4); // Arange length + Asm->OutStreamer->EmitLabel(BeginLabel); + Asm->EmitInt16(dwarf::DW_ARANGES_VERSION); // Version number + Asm->EmitInt32(Unit.getStartOffset()); // Corresponding unit's offset + Asm->EmitInt8(AddressSize); // Address size + Asm->EmitInt8(0); // Segment size + + Asm->OutStreamer->EmitFill(Padding, 0x0); + + for (auto Range = Ranges.begin(), End = Ranges.end(); Range != End; + ++Range) { + uint64_t RangeStart = Range->first; + MS->EmitIntValue(RangeStart, AddressSize); + while ((Range + 1) != End && Range->second == (Range + 1)->first) + ++Range; + MS->EmitIntValue(Range->second - RangeStart, AddressSize); + } + + // Emit terminator + Asm->OutStreamer->EmitIntValue(0, AddressSize); + Asm->OutStreamer->EmitIntValue(0, AddressSize); + Asm->OutStreamer->EmitLabel(EndLabel); + } + + if (!DoDebugRanges) + return; + + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfRangesSection()); + // Offset each range by the right amount. + int64_t PcOffset = -Unit.getLowPc(); + // Emit coalesced ranges. + for (auto Range = Ranges.begin(), End = Ranges.end(); Range != End; ++Range) { + MS->EmitIntValue(Range->first + PcOffset, AddressSize); + while (Range + 1 != End && Range->second == (Range + 1)->first) + ++Range; + MS->EmitIntValue(Range->second + PcOffset, AddressSize); + RangesSectionSize += 2 * AddressSize; + } + + // Add the terminator entry. + MS->EmitIntValue(0, AddressSize); + MS->EmitIntValue(0, AddressSize); + RangesSectionSize += 2 * AddressSize; +} + +/// \brief Emit location lists for \p Unit and update attribtues to +/// point to the new entries. +void DwarfStreamer::emitLocationsForUnit(const CompileUnit &Unit, + DWARFContext &Dwarf) { + const std::vector<std::pair<DIEInteger *, int64_t>> &Attributes = + Unit.getLocationAttributes(); + + if (Attributes.empty()) + return; + + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLocSection()); + + unsigned AddressSize = Unit.getOrigUnit().getAddressByteSize(); + const DWARFSection &InputSec = Dwarf.getLocSection(); + DataExtractor Data(InputSec.Data, Dwarf.isLittleEndian(), AddressSize); + DWARFUnit &OrigUnit = Unit.getOrigUnit(); + const auto *OrigUnitDie = OrigUnit.getUnitDIE(false); + int64_t UnitPcOffset = 0; + uint64_t OrigLowPc = OrigUnitDie->getAttributeValueAsAddress( + &OrigUnit, dwarf::DW_AT_low_pc, -1ULL); + if (OrigLowPc != -1ULL) + UnitPcOffset = int64_t(OrigLowPc) - Unit.getLowPc(); + + for (const auto &Attr : Attributes) { + uint32_t Offset = Attr.first->getValue(); + Attr.first->setValue(LocSectionSize); + // This is the quantity to add to the old location address to get + // the correct address for the new one. + int64_t LocPcOffset = Attr.second + UnitPcOffset; + while (Data.isValidOffset(Offset)) { + uint64_t Low = Data.getUnsigned(&Offset, AddressSize); + uint64_t High = Data.getUnsigned(&Offset, AddressSize); + LocSectionSize += 2 * AddressSize; + if (Low == 0 && High == 0) { + Asm->OutStreamer->EmitIntValue(0, AddressSize); + Asm->OutStreamer->EmitIntValue(0, AddressSize); + break; + } + Asm->OutStreamer->EmitIntValue(Low + LocPcOffset, AddressSize); + Asm->OutStreamer->EmitIntValue(High + LocPcOffset, AddressSize); + uint64_t Length = Data.getU16(&Offset); + Asm->OutStreamer->EmitIntValue(Length, 2); + // Just copy the bytes over. + Asm->OutStreamer->EmitBytes( + StringRef(InputSec.Data.substr(Offset, Length))); + Offset += Length; + LocSectionSize += Length + 2; + } + } +} + +void DwarfStreamer::emitLineTableForUnit(StringRef PrologueBytes, + unsigned MinInstLength, + std::vector<DWARFDebugLine::Row> &Rows, + unsigned PointerSize) { + // Switch to the section where the table will be emitted into. + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLineSection()); + MCSymbol *LineStartSym = MC->createTempSymbol(); + MCSymbol *LineEndSym = MC->createTempSymbol(); + + // The first 4 bytes is the total length of the information for this + // compilation unit (not including these 4 bytes for the length). + Asm->EmitLabelDifference(LineEndSym, LineStartSym, 4); + Asm->OutStreamer->EmitLabel(LineStartSym); + // Copy Prologue. + MS->EmitBytes(PrologueBytes); + LineSectionSize += PrologueBytes.size() + 4; + + SmallString<128> EncodingBuffer; + raw_svector_ostream EncodingOS(EncodingBuffer); + + 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); + MS->EmitBytes(EncodingOS.str()); + LineSectionSize += EncodingBuffer.size(); + MS->EmitLabel(LineEndSym); + return; + } + + // Line table state machine fields + unsigned FileNum = 1; + unsigned LastLine = 1; + unsigned Column = 0; + unsigned IsStatement = 1; + unsigned Isa = 0; + uint64_t Address = -1ULL; + + unsigned RowsSinceLastSequence = 0; + + for (unsigned Idx = 0; Idx < Rows.size(); ++Idx) { + auto &Row = Rows[Idx]; + + int64_t AddressDelta; + if (Address == -1ULL) { + MS->EmitIntValue(dwarf::DW_LNS_extended_op, 1); + MS->EmitULEB128IntValue(PointerSize + 1); + MS->EmitIntValue(dwarf::DW_LNE_set_address, 1); + MS->EmitIntValue(Row.Address, PointerSize); + LineSectionSize += 2 + PointerSize + getULEB128Size(PointerSize + 1); + AddressDelta = 0; + } else { + AddressDelta = (Row.Address - Address) / MinInstLength; + } + + // FIXME: code copied and transfromed from + // MCDwarf.cpp::EmitDwarfLineTable. We should find a way to share + // this code, but the current compatibility requirement with + // classic dsymutil makes it hard. Revisit that once this + // requirement is dropped. + + if (FileNum != Row.File) { + FileNum = Row.File; + MS->EmitIntValue(dwarf::DW_LNS_set_file, 1); + MS->EmitULEB128IntValue(FileNum); + LineSectionSize += 1 + getULEB128Size(FileNum); + } + if (Column != Row.Column) { + Column = Row.Column; + MS->EmitIntValue(dwarf::DW_LNS_set_column, 1); + MS->EmitULEB128IntValue(Column); + LineSectionSize += 1 + getULEB128Size(Column); + } + + // FIXME: We should handle the discriminator here, but dsymutil + // doesn' consider it, thus ignore it for now. + + if (Isa != Row.Isa) { + Isa = Row.Isa; + MS->EmitIntValue(dwarf::DW_LNS_set_isa, 1); + MS->EmitULEB128IntValue(Isa); + LineSectionSize += 1 + getULEB128Size(Isa); + } + if (IsStatement != Row.IsStmt) { + IsStatement = Row.IsStmt; + MS->EmitIntValue(dwarf::DW_LNS_negate_stmt, 1); + LineSectionSize += 1; + } + if (Row.BasicBlock) { + MS->EmitIntValue(dwarf::DW_LNS_set_basic_block, 1); + LineSectionSize += 1; + } + + if (Row.PrologueEnd) { + MS->EmitIntValue(dwarf::DW_LNS_set_prologue_end, 1); + LineSectionSize += 1; + } + + if (Row.EpilogueBegin) { + MS->EmitIntValue(dwarf::DW_LNS_set_epilogue_begin, 1); + LineSectionSize += 1; + } + + int64_t LineDelta = int64_t(Row.Line) - LastLine; + if (!Row.EndSequence) { + MCDwarfLineAddr::Encode(*MC, LineDelta, AddressDelta, EncodingOS); + MS->EmitBytes(EncodingOS.str()); + LineSectionSize += EncodingBuffer.size(); + EncodingBuffer.resize(0); + EncodingOS.resync(); + Address = Row.Address; + LastLine = Row.Line; + RowsSinceLastSequence++; + } else { + if (LineDelta) { + MS->EmitIntValue(dwarf::DW_LNS_advance_line, 1); + MS->EmitSLEB128IntValue(LineDelta); + LineSectionSize += 1 + getSLEB128Size(LineDelta); + } + if (AddressDelta) { + MS->EmitIntValue(dwarf::DW_LNS_advance_pc, 1); + MS->EmitULEB128IntValue(AddressDelta); + LineSectionSize += 1 + getULEB128Size(AddressDelta); + } + MCDwarfLineAddr::Encode(*MC, 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; + } + } + + if (RowsSinceLastSequence) { + MCDwarfLineAddr::Encode(*MC, INT64_MAX, 0, EncodingOS); + MS->EmitBytes(EncodingOS.str()); + LineSectionSize += EncodingBuffer.size(); + EncodingBuffer.resize(0); + EncodingOS.resync(); + } + + MS->EmitLabel(LineEndSym); +} + +/// \brief Emit the pubnames or pubtypes section contribution for \p +/// Unit into \p Sec. The data is provided in \p Names. +void DwarfStreamer::emitPubSectionForUnit( + MCSection *Sec, StringRef SecName, const CompileUnit &Unit, + const std::vector<CompileUnit::AccelInfo> &Names) { + if (Names.empty()) + return; + + // Start the dwarf pubnames section. + Asm->OutStreamer->SwitchSection(Sec); + MCSymbol *BeginLabel = Asm->createTempSymbol("pub" + SecName + "_begin"); + MCSymbol *EndLabel = Asm->createTempSymbol("pub" + SecName + "_end"); + + bool HeaderEmitted = false; + // Emit the pubnames for this compilation unit. + for (const auto &Name : Names) { + if (Name.SkipPubSection) + continue; + + if (!HeaderEmitted) { + // Emit the header. + Asm->EmitLabelDifference(EndLabel, BeginLabel, 4); // Length + Asm->OutStreamer->EmitLabel(BeginLabel); + Asm->EmitInt16(dwarf::DW_PUBNAMES_VERSION); // Version + Asm->EmitInt32(Unit.getStartOffset()); // Unit offset + Asm->EmitInt32(Unit.getNextUnitOffset() - Unit.getStartOffset()); // Size + HeaderEmitted = true; + } + Asm->EmitInt32(Name.Die->getOffset()); + Asm->OutStreamer->EmitBytes( + StringRef(Name.Name.data(), Name.Name.size() + 1)); + } + + if (!HeaderEmitted) + return; + Asm->EmitInt32(0); // End marker. + Asm->OutStreamer->EmitLabel(EndLabel); +} + +/// \brief Emit .debug_pubnames for \p Unit. +void DwarfStreamer::emitPubNamesForUnit(const CompileUnit &Unit) { + emitPubSectionForUnit(MC->getObjectFileInfo()->getDwarfPubNamesSection(), + "names", Unit, Unit.getPubnames()); +} + +/// \brief Emit .debug_pubtypes for \p Unit. +void DwarfStreamer::emitPubTypesForUnit(const CompileUnit &Unit) { + emitPubSectionForUnit(MC->getObjectFileInfo()->getDwarfPubTypesSection(), + "types", Unit, Unit.getPubtypes()); +} + +/// \brief The core of the Dwarf linking logic. +/// +/// The link of the dwarf information from the object files will be +/// driven by the selection of 'root DIEs', which are DIEs that +/// describe variables or functions that are present in the linked +/// binary (and thus have entries in the debug map). All the debug +/// information that will be linked (the DIEs, but also the line +/// tables, ranges, ...) is derived from that set of root DIEs. +/// +/// The root DIEs are identified because they contain relocations that +/// correspond to a debug map entry at specific places (the low_pc for +/// a function, the location for a variable). These relocations are +/// called ValidRelocs in the DwarfLinker and are gathered as a very +/// first step when we start processing a DebugMapObject. +class DwarfLinker { +public: + DwarfLinker(StringRef OutputFilename, const LinkOptions &Options) + : OutputFilename(OutputFilename), Options(Options), + BinHolder(Options.Verbose) {} + + ~DwarfLinker() { + for (auto *Abbrev : Abbreviations) + delete Abbrev; + } + + /// \brief Link the contents of the DebugMap. + bool link(const DebugMap &); + +private: + /// \brief Called at the start of a debug object link. + void startDebugObject(DWARFContext &, DebugMapObject &); + + /// \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; + + 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; } + }; + + /// \brief The valid relocations for the current DebugMapObject. + /// This vector is sorted by relocation offset. + std::vector<ValidReloc> ValidRelocs; + + /// \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 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); + /// @} + + /// \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, + const DebugMapObject &DMO, CompileUnit &CU, + unsigned Flags); + + /// \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. + }; + + /// \brief Mark the passed DIE as well as all the ones it depends on + /// as kept. + void keepDIEAndDenpendencies(const DWARFDebugInfoEntryMinimal &DIE, + CompileUnit::DIEInfo &MyInfo, + const DebugMapObject &DMO, CompileUnit &CU, + unsigned Flags); + + unsigned shouldKeepDIE(const DWARFDebugInfoEntryMinimal &DIE, + CompileUnit &Unit, CompileUnit::DIEInfo &MyInfo, + unsigned Flags); + + unsigned shouldKeepVariableDIE(const DWARFDebugInfoEntryMinimal &DIE, + CompileUnit &Unit, + CompileUnit::DIEInfo &MyInfo, unsigned Flags); + + unsigned shouldKeepSubprogramDIE(const DWARFDebugInfoEntryMinimal &DIE, + CompileUnit &Unit, + CompileUnit::DIEInfo &MyInfo, + unsigned Flags); + + bool hasValidRelocation(uint32_t StartOffset, uint32_t EndOffset, + CompileUnit::DIEInfo &Info); + /// @} + + /// \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; + + /// \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. + + 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), 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); + + /// \brief Helper for cloneDIE. + 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); + + /// \brief Helper for cloneDIE. + 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); + + /// \brief Helper for cloneDIE. + 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); + + /// \brief Assign an abbreviation number to \p Abbrev + void AssignAbbrev(DIEAbbrev &Abbrev); + + /// \brief FoldingSet that uniques the abbreviations. + FoldingSet<DIEAbbrev> AbbreviationsSet; + /// \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; + + /// \brief Compute and emit debug_ranges section for \p Unit, and + /// patch the attributes referencing it. + void patchRangesForUnit(const CompileUnit &Unit, DWARFContext &Dwarf) const; + + /// \brief Generate and emit the DW_AT_ranges attribute for a + /// compile_unit if it had one. + void generateUnitRanges(CompileUnit &Unit) const; + + /// \brief Extract the line tables fromt he original dwarf, extract + /// the relevant parts according to the linked function ranges and + /// emit the result in the debug_line section. + void patchLineTableForUnit(CompileUnit &Unit, DWARFContext &OrigDwarf); + + /// \brief Emit the accelerator entries for \p Unit. + void emitAcceleratorEntriesForUnit(CompileUnit &Unit); + + /// \brief DIELoc objects that need to be destructed (but not freed!). + std::vector<DIELoc *> DIELocs; + /// \brief DIEBlock objects that need to be destructed (but not freed!). + std::vector<DIEBlock *> DIEBlocks; + /// \brief Allocator used for all the DIEValue objects. + BumpPtrAllocator DIEAlloc; + /// @} + + /// \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); + /// @} + +private: + std::string OutputFilename; + LinkOptions Options; + BinaryHolder BinHolder; + std::unique_ptr<DwarfStreamer> Streamer; + + /// The units of the current debug map object. + std::vector<CompileUnit> Units; + + /// The debug map object curently under consideration. + DebugMapObject *CurrentDebugObject; + + /// \brief The Dwarf string pool + NonRelocatableStringpool StringPool; + + /// \brief This map is keyed by the entry PC of functions in that + /// debug object and the associated value is a pair storing the + /// corresponding end PC and the offset to apply to get the linked + /// address. + /// + /// See startDebugObject() for a more complete description of its use. + std::map<uint64_t, std::pair<uint64_t, int64_t>> Ranges; +}; + +/// \brief Similar to DWARFUnitSection::getUnitForOffset(), but +/// returning our CompileUnit object instead. +CompileUnit *DwarfLinker::getUnitForOffset(unsigned Offset) { + auto CU = + std::upper_bound(Units.begin(), Units.end(), Offset, + [](uint32_t LHS, const CompileUnit &RHS) { + return LHS < RHS.getOrigUnit().getNextUnitOffset(); + }); + return CU != Units.end() ? &*CU : nullptr; +} + +/// \brief 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, + const DWARFDebugInfoEntryMinimal &DIE, CompileUnit *&RefCU) { + assert(RefValue.isFormClass(DWARFFormValue::FC_Reference)); + uint64_t RefOffset = *RefValue.getAsReference(&Unit); + + if ((RefCU = getUnitForOffset(RefOffset))) + if (const auto *RefDie = RefCU->getOrigUnit().getDIEForOffset(RefOffset)) + return RefDie; + + 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 + // short name. + if (!Info.MangledName && + (Info.MangledName = Die.getName(&U, DINameKind::LinkageName))) + Info.MangledNameOffset = StringPool.getStringOffset(Info.MangledName); + + if (!Info.Name && (Info.Name = Die.getName(&U, DINameKind::ShortName))) + Info.NameOffset = StringPool.getStringOffset(Info.Name); + + return Info.Name || Info.MangledName; +} + +/// \brief Report a warning to the user, optionaly including +/// information about a specific \p DIE related to the warning. +void DwarfLinker::reportWarning(const Twine &Warning, const DWARFUnit *Unit, + const DWARFDebugInfoEntryMinimal *DIE) const { + StringRef Context = "<debug map>"; + if (CurrentDebugObject) + Context = CurrentDebugObject->getObjectFilename(); + warn(Warning, Context); + + if (!Options.Verbose || !DIE) + return; + + errs() << " in DIE:\n"; + DIE->dump(errs(), const_cast<DWARFUnit *>(Unit), 0 /* RecurseDepth */, + 6 /* Indent */); +} + +bool DwarfLinker::createStreamer(Triple TheTriple, StringRef OutputFilename) { + if (Options.NoOutput) + return true; + + Streamer = llvm::make_unique<DwarfStreamer>(); + 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) { + unsigned MyIdx = CU.getOrigUnit().getDIEIndex(DIE); + CU.getInfo(MyIdx).ParentIdx = ParentIdx; + + if (DIE->hasChildren()) + for (auto *Child = DIE->getFirstChild(); Child && !Child->isNULL(); + Child = Child->getSibling()) + gatherDIEParents(Child, MyIdx, CU); +} + +static bool dieNeedsChildrenToBeMeaningful(uint32_t Tag) { + switch (Tag) { + default: + return false; + case dwarf::DW_TAG_subprogram: + case dwarf::DW_TAG_lexical_block: + case dwarf::DW_TAG_subroutine_type: + case dwarf::DW_TAG_structure_type: + case dwarf::DW_TAG_class_type: + case dwarf::DW_TAG_union_type: + return true; + } + llvm_unreachable("Invalid Tag"); +} + +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 + // unit, with 2 notable differences: + // - obviously this one is global, while the other ones are per-unit. + // - this one contains not only the functions described in the DIE + // tree, but also the ones that are only in the debug map. + // The latter information is required to reproduce dsymutil's logic + // while linking line tables. The cases where this information + // matters look like bugs that need to be investigated, but for now + // we need to reproduce dsymutil's behavior. + // FIXME: Once we understood exactly if that information is needed, + // maybe totally remove this (or try to use it to do a real + // -gline-tables-only on Darwin. + for (const auto &Entry : Obj.symbols()) { + const auto &Mapping = Entry.getValue(); + if (Mapping.Size) + Ranges[Mapping.ObjectAddress] = std::make_pair( + Mapping.ObjectAddress + Mapping.Size, + int64_t(Mapping.BinaryAddress) - Mapping.ObjectAddress); + } +} + +void DwarfLinker::endDebugObject() { + Units.clear(); + ValidRelocs.clear(); + Ranges.clear(); + + for (auto *Block : DIEBlocks) + Block->~DIEBlock(); + for (auto *Loc : DIELocs) + Loc->~DIELoc(); + + DIEBlocks.clear(); + DIELocs.clear(); + DIEAlloc.Reset(); +} + +/// \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) { + StringRef Contents; + Section.getContents(Contents); + DataExtractor Data(Contents, Obj.isLittleEndian(), 0); + + for (const object::RelocationRef &Reloc : Section.relocations()) { + object::DataRefImpl RelocDataRef = Reloc.getRawDataRefImpl(); + MachO::any_relocation_info MachOReloc = Obj.getRelocation(RelocDataRef); + unsigned RelocSize = 1 << Obj.getAnyRelocationLength(MachOReloc); + uint64_t Offset64; + if ((RelocSize != 4 && RelocSize != 8) || Reloc.getOffset(Offset64)) { + reportWarning(" unsupported relocation in debug_info section."); + continue; + } + uint32_t Offset = Offset64; + // Mach-o uses REL relocations, the addend is at the relocation offset. + uint64_t Addend = Data.getUnsigned(&Offset, RelocSize); + + auto Sym = Reloc.getSymbol(); + if (Sym != Obj.symbol_end()) { + StringRef SymbolName; + if (Sym->getName(SymbolName)) { + reportWarning("error getting relocation symbol name."); + continue; + } + if (const auto *Mapping = DMO.lookupSymbol(SymbolName)) + ValidRelocs.emplace_back(Offset64, RelocSize, Addend, Mapping); + } else if (const auto *Mapping = DMO.lookupObjectAddress(Addend)) { + // Do not store the addend. The addend was the address of the + // symbol in the object file, the address in the binary that is + // stored in the debug map doesn't need to be offseted. + ValidRelocs.emplace_back(Offset64, RelocSize, 0, Mapping); + } + } +} + +/// \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) { + // 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()); + + if (ValidRelocs.empty()) + return false; + + // Sort the relocations by offset. We will walk the DIEs linearly in + // the file, this allows us to just keep an index in the relocation + // array that we advance during our walk, rather than resorting to + // some associative container. See DwarfLinker::NextValidReloc. + std::sort(ValidRelocs.begin(), ValidRelocs.end()); + return true; +} + +/// \brief Look for relocations in the debug_info section that match +/// entries in the debug map. These relocations will drive the Dwarf +/// 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) { + // Find the debug_info section. + for (const object::SectionRef &Section : Obj.sections()) { + StringRef SectionName; + Section.getName(SectionName); + SectionName = SectionName.substr(SectionName.find_first_not_of("._")); + if (SectionName != "debug_info") + continue; + return findValidRelocs(Section, Obj, DMO); + } + return false; +} + +/// \brief Checks that there is a relocation against an actual debug +/// map entry between \p StartOffset and \p NextOffset. +/// +/// 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) { + assert(NextValidReloc == 0 || + StartOffset > ValidRelocs[NextValidReloc - 1].Offset); + if (NextValidReloc >= ValidRelocs.size()) + return false; + + uint64_t RelocOffset = ValidRelocs[NextValidReloc].Offset; + + // We might need to skip some relocs that we didn't consider. For + // example the high_pc of a discarded DIE might contain a reloc that + // is in the list because it actually corresponds to the start of a + // function that is in the debug map. + while (RelocOffset < StartOffset && NextValidReloc < ValidRelocs.size() - 1) + RelocOffset = ValidRelocs[++NextValidReloc].Offset; + + if (RelocOffset < StartOffset || RelocOffset >= EndOffset) + return false; + + const auto &ValidReloc = ValidRelocs[NextValidReloc++]; + if (Options.Verbose) + outs() << "Found valid debug map entry: " << ValidReloc.Mapping->getKey() + << " " << format("\t%016" PRIx64 " => %016" PRIx64, + ValidReloc.Mapping->getValue().ObjectAddress, + ValidReloc.Mapping->getValue().BinaryAddress); + + Info.AddrAdjust = int64_t(ValidReloc.Mapping->getValue().BinaryAddress) + + ValidReloc.Addend - + ValidReloc.Mapping->getValue().ObjectAddress; + Info.InDebugMap = true; + return true; +} + +/// \brief Get the starting and ending (exclusive) offset for the +/// attribute with index \p Idx descibed by \p Abbrev. \p Offset is +/// supposed to point to the position of the first attribute described +/// by \p Abbrev. +/// \return [StartOffset, EndOffset) as a pair. +static std::pair<uint32_t, uint32_t> +getAttributeOffsets(const DWARFAbbreviationDeclaration *Abbrev, unsigned Idx, + unsigned Offset, const DWARFUnit &Unit) { + DataExtractor Data = Unit.getDebugInfoExtractor(); + + for (unsigned i = 0; i < Idx; ++i) + DWARFFormValue::skipValue(Abbrev->getFormByIndex(i), Data, &Offset, &Unit); + + uint32_t End = Offset; + DWARFFormValue::skipValue(Abbrev->getFormByIndex(Idx), Data, &End, &Unit); + + return std::make_pair(Offset, End); +} + +/// \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) { + const auto *Abbrev = DIE.getAbbreviationDeclarationPtr(); + + // Global variables with constant value can always be kept. + if (!(Flags & TF_InFunctionScope) && + Abbrev->findAttributeIndex(dwarf::DW_AT_const_value) != -1U) { + MyInfo.InDebugMap = true; + return Flags | TF_Keep; + } + + uint32_t LocationIdx = Abbrev->findAttributeIndex(dwarf::DW_AT_location); + if (LocationIdx == -1U) + return Flags; + + uint32_t Offset = DIE.getOffset() + getULEB128Size(Abbrev->getCode()); + const DWARFUnit &OrigUnit = Unit.getOrigUnit(); + uint32_t LocationOffset, LocationEndOffset; + std::tie(LocationOffset, LocationEndOffset) = + getAttributeOffsets(Abbrev, LocationIdx, Offset, OrigUnit); + + // See if there is a relocation to a valid debug map entry inside + // this variable's location. The order is important here. We want to + // 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) || + (Flags & TF_InFunctionScope)) + return Flags; + + if (Options.Verbose) + DIE.dump(outs(), const_cast<DWARFUnit *>(&OrigUnit), 0, 8 /* Indent */); + + return Flags | TF_Keep; +} + +/// \brief Check if a function describing DIE should be kept. +/// \returns updated TraversalFlags. +unsigned DwarfLinker::shouldKeepSubprogramDIE( + const DWARFDebugInfoEntryMinimal &DIE, CompileUnit &Unit, + CompileUnit::DIEInfo &MyInfo, unsigned Flags) { + const auto *Abbrev = DIE.getAbbreviationDeclarationPtr(); + + Flags |= TF_InFunctionScope; + + uint32_t LowPcIdx = Abbrev->findAttributeIndex(dwarf::DW_AT_low_pc); + if (LowPcIdx == -1U) + return Flags; + + uint32_t Offset = DIE.getOffset() + getULEB128Size(Abbrev->getCode()); + const DWARFUnit &OrigUnit = Unit.getOrigUnit(); + uint32_t LowPcOffset, LowPcEndOffset; + std::tie(LowPcOffset, LowPcEndOffset) = + getAttributeOffsets(Abbrev, LowPcIdx, Offset, OrigUnit); + + uint64_t LowPc = + 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)) + return Flags; + + if (Options.Verbose) + DIE.dump(outs(), const_cast<DWARFUnit *>(&OrigUnit), 0, 8 /* Indent */); + + Flags |= TF_Keep; + + DWARFFormValue HighPcValue; + if (!DIE.getAttributeValue(&OrigUnit, dwarf::DW_AT_high_pc, HighPcValue)) { + reportWarning("Function without high_pc. Range will be discarded.\n", + &OrigUnit, &DIE); + return Flags; + } + + uint64_t HighPc; + if (HighPcValue.isFormClass(DWARFFormValue::FC_Address)) { + HighPc = *HighPcValue.getAsAddress(&OrigUnit); + } else { + assert(HighPcValue.isFormClass(DWARFFormValue::FC_Constant)); + HighPc = LowPc + *HighPcValue.getAsUnsignedConstant(); + } + + // Replace the debug map range with a more accurate one. + Ranges[LowPc] = std::make_pair(HighPc, MyInfo.AddrAdjust); + Unit.addFunctionRange(LowPc, HighPc, MyInfo.AddrAdjust); + return Flags; +} + +/// \brief Check if a DIE should be kept. +/// \returns updated TraversalFlags. +unsigned DwarfLinker::shouldKeepDIE(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); + case dwarf::DW_TAG_subprogram: + return shouldKeepSubprogramDIE(DIE, Unit, MyInfo, Flags); + case dwarf::DW_TAG_module: + case dwarf::DW_TAG_imported_module: + case dwarf::DW_TAG_imported_declaration: + case dwarf::DW_TAG_imported_unit: + // We always want to keep these. + return Flags | TF_Keep; + } + + return Flags; +} + +/// \brief Mark the passed DIE as well as all the ones it depends on +/// as kept. +/// +/// This function is called by lookForDIEsToKeep on DIEs that are +/// newly discovered to be needed in the link. It recursively calls +/// 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, + CompileUnit::DIEInfo &MyInfo, + const DebugMapObject &DMO, + CompileUnit &CU, unsigned Flags) { + 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); + 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()); + + // Mark all DIEs referenced through atttributes as kept. + for (const auto &AttrSpec : Abbrev->attributes()) { + DWARFFormValue Val(AttrSpec.Form); + + if (!Val.isFormClass(DWARFFormValue::FC_Reference)) { + DWARFFormValue::skipValue(AttrSpec.Form, Data, &Offset, &Unit); + continue; + } + + Val.extractValue(Data, &Offset, &Unit); + CompileUnit *ReferencedCU; + if (const auto *RefDIE = resolveDIEReference(Val, Unit, DIE, ReferencedCU)) + lookForDIEsToKeep(*RefDIE, DMO, *ReferencedCU, + TF_Keep | TF_DependencyWalk); + } +} + +/// \brief Recursively walk the \p DIE tree and look for DIEs to +/// keep. Store that information in \p CU's DIEInfo. +/// +/// This function is the entry point of the DIE selection +/// algorithm. It is expected to walk the DIE tree in file order and +/// (though the mediation of its helper) call hasValidRelocation() on +/// each DIE that might be a 'root DIE' (See DwarfLinker class +/// comment). +/// While walking the dependencies of root DIEs, this function is +/// 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, + const DebugMapObject &DMO, CompileUnit &CU, + unsigned Flags) { + unsigned Idx = CU.getOrigUnit().getDIEIndex(&DIE); + CompileUnit::DIEInfo &MyInfo = CU.getInfo(Idx); + bool AlreadyKept = MyInfo.Keep; + + // 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 + // set. + if ((Flags & TF_DependencyWalk) && AlreadyKept) + return; + + // We must not call shouldKeepDIE while called from keepDIEAndDenpendencies, + // because it would screw up the relocation finding logic. + if (!(Flags & TF_DependencyWalk)) + Flags = shouldKeepDIE(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); + + // 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())) + Flags &= ~TF_ParentWalk; + + if (!DIE.hasChildren() || (Flags & TF_ParentWalk)) + return; + + for (auto *Child = DIE.getFirstChild(); Child && !Child->isNULL(); + Child = Child->getSibling()) + lookForDIEsToKeep(*Child, DMO, CU, Flags); +} + +/// \brief Assign an abbreviation numer to \p Abbrev. +/// +/// Our DIEs get freed after every DebugMapObject has been processed, +/// thus the FoldingSet we use to unique DIEAbbrevs cannot refer to +/// the instances hold by the DIEs. When we encounter an abbreviation +/// that we don't know, we create a permanent copy of it. +void DwarfLinker::AssignAbbrev(DIEAbbrev &Abbrev) { + // Check the set for priors. + FoldingSetNodeID ID; + Abbrev.Profile(ID); + void *InsertToken; + DIEAbbrev *InSet = AbbreviationsSet.FindNodeOrInsertPos(ID, InsertToken); + + // If it's newly added. + if (InSet) { + // Assign existing abbreviation number. + Abbrev.setNumber(InSet->getNumber()); + } else { + // Add to abbreviation list. + Abbreviations.push_back( + new DIEAbbrev(Abbrev.getTag(), Abbrev.hasChildren())); + for (const auto &Attr : Abbrev.getData()) + Abbreviations.back()->AddAttribute(Attr.getAttribute(), Attr.getForm()); + AbbreviationsSet.InsertNode(Abbreviations.back(), 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) { + // Switch everything to out of line strings. + const char *String = *Val.getAsCString(&U); + unsigned Offset = StringPool.getStringOffset(String); + Die.addValue(dwarf::Attribute(AttrSpec.Attr), dwarf::DW_FORM_strp, + new (DIEAlloc) 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( + DIE &Die, const DWARFDebugInfoEntryMinimal &InputDIE, + AttributeSpec AttrSpec, unsigned AttrSize, const DWARFFormValue &Val, + CompileUnit &Unit) { + uint32_t Ref = *Val.getAsReference(&Unit.getOrigUnit()); + DIE *NewRefDie = nullptr; + CompileUnit *RefUnit = nullptr; + const DWARFDebugInfoEntryMinimal *RefDie = 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); + return 0; + } + + unsigned Idx = RefUnit->getOrigUnit().getDIEIndex(RefDie); + CompileUnit::DIEInfo &RefInfo = RefUnit->getInfo(Idx); + if (!RefInfo.Clone) { + assert(Ref > InputDIE.getOffset()); + // We haven't cloned this DIE yet. Just create an empty one and + // store it. It'll get really cloned when we process it. + RefInfo.Clone = new DIE(dwarf::Tag(RefDie->getTag())); + } + NewRefDie = RefInfo.Clone; + + if (AttrSpec.Form == dwarf::DW_FORM_ref_addr) { + // 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) + // FIXME: we should be able to design DIEEntry reliance on + // DwarfDebug away. + DIEInteger *Attr; + if (Ref < InputDIE.getOffset()) { + // We must have already cloned that DIE. + uint32_t NewRefOffset = + RefUnit->getStartOffset() + NewRefDie->getOffset(); + Attr = new (DIEAlloc) DIEInteger(NewRefOffset); + } else { + // A forward reference. Note and fixup later. + Attr = new (DIEAlloc) DIEInteger(0xBADDEF); + Unit.noteForwardReference(NewRefDie, RefUnit, Attr); + } + Die.addValue(dwarf::Attribute(AttrSpec.Attr), dwarf::DW_FORM_ref_addr, + Attr); + return AttrSize; + } + + Die.addValue(dwarf::Attribute(AttrSpec.Attr), dwarf::Form(AttrSpec.Form), + new (DIEAlloc) DIEEntry(*NewRefDie)); + 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; + 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); + } else { + Block = new (DIEAlloc) DIEBlock(); + DIEBlocks.push_back(Block); + } + Attr = Loc ? static_cast<DIE *>(Loc) : static_cast<DIE *>(Block); + Value = Loc ? static_cast<DIEValue *>(Loc) : static_cast<DIEValue *>(Block); + ArrayRef<uint8_t> Bytes = *Val.getAsBlock(); + for (auto Byte : Bytes) + Attr->addValue(static_cast<dwarf::Attribute>(0), dwarf::DW_FORM_data1, + new (DIEAlloc) DIEInteger(Byte)); + // 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 (Loc) + Loc->ComputeSize(&Streamer->getAsmPrinter()); + else + Block->ComputeSize(&Streamer->getAsmPrinter()); + } + Die.addValue(dwarf::Attribute(AttrSpec.Attr), dwarf::Form(AttrSpec.Form), + 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) { + 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; + else if (Die.getTag() == dwarf::DW_TAG_compile_unit) { + Addr = Unit.getLowPc(); + if (Addr == UINT64_MAX) + return 0; + } + Info.HasLowPc = true; + } else if (AttrSpec.Attr == dwarf::DW_AT_high_pc) { + if (Die.getTag() == dwarf::DW_TAG_compile_unit) { + if (uint64_t HighPc = Unit.getHighPc()) + Addr = HighPc; + else + return 0; + } else + // If we have a high_pc recorded for the input DIE, use + // it. Otherwise (when no relocations where applied) just use the + // one we just decoded. + Addr = (Info.OrigHighPc ? Info.OrigHighPc : Addr) + Info.PCOffset; + } + + Die.addValue(static_cast<dwarf::Attribute>(AttrSpec.Attr), + static_cast<dwarf::Form>(AttrSpec.Form), + new (DIEAlloc) DIEInteger(Addr)); + 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( + DIE &Die, const DWARFDebugInfoEntryMinimal &InputDIE, CompileUnit &Unit, + AttributeSpec AttrSpec, const DWARFFormValue &Val, unsigned AttrSize, + AttributesInfo &Info) { + uint64_t Value; + if (AttrSpec.Attr == dwarf::DW_AT_high_pc && + Die.getTag() == dwarf::DW_TAG_compile_unit) { + if (Unit.getLowPc() == -1ULL) + return 0; + // Dwarf >= 4 high_pc is an size, not an address. + Value = Unit.getHighPc() - Unit.getLowPc(); + } else if (AttrSpec.Form == dwarf::DW_FORM_sec_offset) + Value = *Val.getAsSectionOffset(); + else if (AttrSpec.Form == dwarf::DW_FORM_sdata) + Value = *Val.getAsSignedConstant(); + else if (auto OptionalValue = Val.getAsUnsignedConstant()) + Value = *OptionalValue; + else { + reportWarning("Unsupported scalar attribute form. Dropping attribute.", + &Unit.getOrigUnit(), &InputDIE); + return 0; + } + DIEInteger *Attr = new (DIEAlloc) DIEInteger(Value); + if (AttrSpec.Attr == dwarf::DW_AT_ranges) + Unit.noteRangeAttribute(Die, Attr); + // 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. + else if (AttrSpec.Attr == dwarf::DW_AT_location || + AttrSpec.Attr == dwarf::DW_AT_frame_base) + Unit.noteLocationAttribute(Attr, Info.PCOffset); + else if (AttrSpec.Attr == dwarf::DW_AT_declaration && Value) + Info.IsDeclaration = true; + + Die.addValue(dwarf::Attribute(AttrSpec.Attr), dwarf::Form(AttrSpec.Form), + Attr); + return AttrSize; +} + +/// \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) { + const DWARFUnit &U = Unit.getOrigUnit(); + + switch (AttrSpec.Form) { + case dwarf::DW_FORM_strp: + case dwarf::DW_FORM_string: + return cloneStringAttribute(Die, AttrSpec, Val, U); + case dwarf::DW_FORM_ref_addr: + case dwarf::DW_FORM_ref1: + case dwarf::DW_FORM_ref2: + case dwarf::DW_FORM_ref4: + case dwarf::DW_FORM_ref8: + return cloneDieReferenceAttribute(Die, InputDIE, AttrSpec, AttrSize, Val, + Unit); + case dwarf::DW_FORM_block: + case dwarf::DW_FORM_block1: + case dwarf::DW_FORM_block2: + case dwarf::DW_FORM_block4: + case dwarf::DW_FORM_exprloc: + return cloneBlockAttribute(Die, AttrSpec, Val, AttrSize); + case dwarf::DW_FORM_addr: + return cloneAddressAttribute(Die, AttrSpec, Val, Unit, Info); + case dwarf::DW_FORM_data1: + case dwarf::DW_FORM_data2: + case dwarf::DW_FORM_data4: + case dwarf::DW_FORM_data8: + case dwarf::DW_FORM_udata: + case dwarf::DW_FORM_sdata: + case dwarf::DW_FORM_sec_offset: + case dwarf::DW_FORM_flag: + case dwarf::DW_FORM_flag_present: + return cloneScalarAttribute(Die, InputDIE, Unit, AttrSpec, Val, AttrSize, + Info); + default: + reportWarning("Unsupported attribute form in cloneAttribute. Dropping.", &U, + &InputDIE); + } + + return 0; +} + +/// \brief Apply the valid relocations found by findValidRelocs() to +/// the buffer \p Data, taking into account that Data is at \p BaseOffset +/// in the debug_info section. +/// +/// Like for findValidRelocs(), this function must be called with +/// monotonic \p BaseOffset values. +/// +/// \returns wether any reloc has been applied. +bool DwarfLinker::applyValidRelocs(MutableArrayRef<char> Data, + uint32_t BaseOffset, bool isLittleEndian) { + assert((NextValidReloc == 0 || + BaseOffset > ValidRelocs[NextValidReloc - 1].Offset) && + "BaseOffset should only be increasing."); + if (NextValidReloc >= ValidRelocs.size()) + return false; + + // Skip relocs that haven't been applied. + while (NextValidReloc < ValidRelocs.size() && + ValidRelocs[NextValidReloc].Offset < BaseOffset) + ++NextValidReloc; + + bool Applied = false; + uint64_t EndOffset = BaseOffset + Data.size(); + while (NextValidReloc < ValidRelocs.size() && + ValidRelocs[NextValidReloc].Offset >= BaseOffset && + ValidRelocs[NextValidReloc].Offset < EndOffset) { + const auto &ValidReloc = ValidRelocs[NextValidReloc++]; + assert(ValidReloc.Offset - BaseOffset < Data.size()); + assert(ValidReloc.Offset - BaseOffset + ValidReloc.Size <= Data.size()); + char Buf[8]; + uint64_t Value = ValidReloc.Mapping->getValue().BinaryAddress; + Value += ValidReloc.Addend; + for (unsigned i = 0; i != ValidReloc.Size; ++i) { + unsigned Index = isLittleEndian ? i : (ValidReloc.Size - i - 1); + Buf[i] = uint8_t(Value >> (Index * 8)); + } + assert(ValidReloc.Size <= sizeof(Buf)); + memcpy(&Data[ValidReloc.Offset - BaseOffset], Buf, ValidReloc.Size); + Applied = true; + } + + return Applied; +} + +static bool isTypeTag(uint16_t Tag) { + switch (Tag) { + case dwarf::DW_TAG_array_type: + case dwarf::DW_TAG_class_type: + case dwarf::DW_TAG_enumeration_type: + case dwarf::DW_TAG_pointer_type: + case dwarf::DW_TAG_reference_type: + case dwarf::DW_TAG_string_type: + case dwarf::DW_TAG_structure_type: + case dwarf::DW_TAG_subroutine_type: + case dwarf::DW_TAG_typedef: + case dwarf::DW_TAG_union_type: + case dwarf::DW_TAG_ptr_to_member_type: + case dwarf::DW_TAG_set_type: + case dwarf::DW_TAG_subrange_type: + case dwarf::DW_TAG_base_type: + case dwarf::DW_TAG_const_type: + case dwarf::DW_TAG_constant: + case dwarf::DW_TAG_file_type: + case dwarf::DW_TAG_namelist: + case dwarf::DW_TAG_packed_type: + case dwarf::DW_TAG_volatile_type: + case dwarf::DW_TAG_restrict_type: + case dwarf::DW_TAG_interface_type: + case dwarf::DW_TAG_unspecified_type: + case dwarf::DW_TAG_shared_type: + return true; + default: + break; + } + 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) { + DWARFUnit &U = Unit.getOrigUnit(); + unsigned Idx = U.getDIEIndex(&InputDIE); + CompileUnit::DIEInfo &Info = Unit.getInfo(Idx); + + // Should the DIE appear in the output? + if (!Unit.getInfo(Idx).Keep) + return nullptr; + + uint32_t Offset = InputDIE.getOffset(); + // The DIE might have been already created by a forward reference + // (see cloneDieReferenceAttribute()). + DIE *Die = Info.Clone; + if (!Die) + Die = Info.Clone = new DIE(dwarf::Tag(InputDIE.getTag())); + assert(Die->getTag() == InputDIE.getTag()); + Die->setOffset(OutOffset); + + // Extract and clone every attribute. + DataExtractor Data = U.getDebugInfoExtractor(); + uint32_t NextOffset = U.getDIEAtIndex(Idx + 1)->getOffset(); + AttributesInfo AttrInfo; + + // We could copy the data only if we need to aply a relocation to + // it. After testing, it seems there is no performance downside to + // doing the copy unconditionally, and it makes the code simpler. + 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 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 + // totally unrelated value (because the end address in the object + // file might be start address of another function which got moved + // independantly by the linker). The computation of the actual + // high_pc value is done in cloneAddressAttribute(). + AttrInfo.OrigHighPc = + InputDIE.getAttributeValueAsAddress(&U, dwarf::DW_AT_high_pc, 0); + } + + // Reset the Offset to 0 as we will be working on the local copy of + // the data. + Offset = 0; + + const auto *Abbrev = InputDIE.getAbbreviationDeclarationPtr(); + Offset += getULEB128Size(Abbrev->getCode()); + + // We are entering a subprogram. Get and propagate the PCOffset. + if (Die->getTag() == dwarf::DW_TAG_subprogram) + PCOffset = Info.AddrAdjust; + AttrInfo.PCOffset = PCOffset; + + for (const auto &AttrSpec : Abbrev->attributes()) { + DWARFFormValue Val(AttrSpec.Form); + uint32_t AttrSize = Offset; + Val.extractValue(Data, &Offset, &U); + AttrSize = Offset - AttrSize; + + OutOffset += + cloneAttribute(*Die, InputDIE, Unit, Val, AttrSpec, AttrSize, AttrInfo); + } + + // Look for accelerator entries. + uint16_t Tag = InputDIE.getTag(); + // FIXME: This is slightly wrong. An inline_subroutine without a + // low_pc, but with AT_ranges might be interesting to get into the + // accelerator tables too. For now stick with dsymutil's behavior. + if ((Info.InDebugMap || AttrInfo.HasLowPc) && + Tag != dwarf::DW_TAG_compile_unit && + getDIENames(InputDIE, Unit.getOrigUnit(), AttrInfo)) { + if (AttrInfo.MangledName && AttrInfo.MangledName != AttrInfo.Name) + Unit.addNameAccelerator(Die, AttrInfo.MangledName, + AttrInfo.MangledNameOffset, + Tag == dwarf::DW_TAG_inlined_subroutine); + if (AttrInfo.Name) + Unit.addNameAccelerator(Die, AttrInfo.Name, AttrInfo.NameOffset, + Tag == dwarf::DW_TAG_inlined_subroutine); + } else if (isTypeTag(Tag) && !AttrInfo.IsDeclaration && + getDIENames(InputDIE, Unit.getOrigUnit(), AttrInfo)) { + Unit.addTypeAccelerator(Die, AttrInfo.Name, AttrInfo.NameOffset); + } + + DIEAbbrev &NewAbbrev = Die->getAbbrev(); + // 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()) + NewAbbrev.setChildrenFlag(dwarf::DW_CHILDREN_yes); + // Assign a permanent abbrev number + AssignAbbrev(Die->getAbbrev()); + + // Add the size of the abbreviation number to the output offset. + OutOffset += getULEB128Size(Die->getAbbrevNumber()); + + if (!Abbrev->hasChildren()) { + // Update our size. + Die->setSize(OutOffset - Die->getOffset()); + return Die; + } + + // Recursively clone children. + for (auto *Child = InputDIE.getFirstChild(); Child && !Child->isNULL(); + Child = Child->getSibling()) { + if (DIE *Clone = cloneDIE(*Child, Unit, PCOffset, OutOffset)) { + Die->addChild(std::unique_ptr<DIE>(Clone)); + OutOffset = Clone->getOffset() + Clone->getSize(); + } + } + + // Account for the end of children marker. + OutOffset += sizeof(int8_t); + // Update our size. + Die->setSize(OutOffset - Die->getOffset()); + return Die; +} + +/// \brief Patch the input object file relevant debug_ranges entries +/// and emit them in the output file. Update the relevant attributes +/// to point at the new entries. +void DwarfLinker::patchRangesForUnit(const CompileUnit &Unit, + DWARFContext &OrigDwarf) const { + DWARFDebugRangeList RangeList; + const auto &FunctionRanges = Unit.getFunctionRanges(); + unsigned AddressSize = Unit.getOrigUnit().getAddressByteSize(); + DataExtractor RangeExtractor(OrigDwarf.getRangeSection(), + OrigDwarf.isLittleEndian(), AddressSize); + auto InvalidRange = FunctionRanges.end(), CurrRange = InvalidRange; + DWARFUnit &OrigUnit = Unit.getOrigUnit(); + const auto *OrigUnitDie = OrigUnit.getUnitDIE(false); + 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. + int64_t UnitPcOffset = 0; + if (OrigLowPc != -1ULL) + UnitPcOffset = int64_t(OrigLowPc) - Unit.getLowPc(); + + for (const auto &RangeAttribute : Unit.getRangesAttributes()) { + uint32_t Offset = RangeAttribute->getValue(); + RangeAttribute->setValue(Streamer->getRangesSectionSize()); + RangeList.extract(RangeExtractor, &Offset); + const auto &Entries = RangeList.getEntries(); + 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; + } + } + + Streamer->emitRangesEntries(UnitPcOffset, OrigLowPc, CurrRange, Entries, + AddressSize); + } +} + +/// \brief Generate the debug_aranges entries for \p Unit and if the +/// unit has a DW_AT_ranges attribute, also emit the debug_ranges +/// contribution for this attribute. +/// FIXME: this could actually be done right in patchRangesForUnit, +/// but for the sake of initial bit-for-bit compatibility with legacy +/// dsymutil, we have to do it in a delayed pass. +void DwarfLinker::generateUnitRanges(CompileUnit &Unit) const { + DIEInteger *Attr = Unit.getUnitRangesAttribute(); + if (Attr) + Attr->setValue(Streamer->getRangesSectionSize()); + Streamer->emitUnitRangesEntries(Unit, Attr != nullptr); +} + +/// \brief Insert the new line info sequence \p Seq into the current +/// set of already linked line info \p Rows. +static void insertLineSequence(std::vector<DWARFDebugLine::Row> &Seq, + std::vector<DWARFDebugLine::Row> &Rows) { + if (Seq.empty()) + return; + + if (!Rows.empty() && Rows.back().Address < Seq.front().Address) { + Rows.insert(Rows.end(), Seq.begin(), Seq.end()); + Seq.clear(); + return; + } + + auto InsertPoint = std::lower_bound( + Rows.begin(), Rows.end(), Seq.front(), + [](const DWARFDebugLine::Row &LHS, const DWARFDebugLine::Row &RHS) { + return LHS.Address < RHS.Address; + }); + + // FIXME: this only removes the unneeded end_sequence if the + // sequences have been inserted in order. using a global sort like + // described in patchLineTableForUnit() and delaying the end_sequene + // elimination to emitLineTableForUnit() we can get rid of all of them. + if (InsertPoint != Rows.end() && + InsertPoint->Address == Seq.front().Address && InsertPoint->EndSequence) { + *InsertPoint = Seq.front(); + Rows.insert(InsertPoint + 1, Seq.begin() + 1, Seq.end()); + } else { + Rows.insert(InsertPoint, Seq.begin(), Seq.end()); + } + + Seq.clear(); +} + +/// \brief Extract the line table for \p Unit from \p OrigDwarf, and +/// recreate a relocated version of these for the address ranges that +/// are present in the binary. +void DwarfLinker::patchLineTableForUnit(CompileUnit &Unit, + DWARFContext &OrigDwarf) { + const DWARFDebugInfoEntryMinimal *CUDie = + Unit.getOrigUnit().getUnitDIE(); + uint64_t StmtList = CUDie->getAttributeValueAsSectionOffset( + &Unit.getOrigUnit(), dwarf::DW_AT_stmt_list, -1ULL); + if (StmtList == -1ULL) + return; + + // Update the cloned DW_AT_stmt_list with the correct debug_line offset. + if (auto *OutputDIE = Unit.getOutputUnitDIE()) { + const auto &Abbrev = OutputDIE->getAbbrev().getData(); + auto Stmt = std::find_if( + Abbrev.begin(), Abbrev.end(), [](const DIEAbbrevData &AbbrevData) { + return AbbrevData.getAttribute() == dwarf::DW_AT_stmt_list; + }); + assert(Stmt < Abbrev.end() && "Didn't find DW_AT_stmt_list in cloned DIE!"); + DIEInteger *StmtAttr = + cast<DIEInteger>(OutputDIE->getValues()[Stmt - Abbrev.begin()]); + StmtAttr->setValue(Streamer->getLineSectionSize()); + } + + // Parse the original line info for the unit. + DWARFDebugLine::LineTable LineTable; + uint32_t StmtOffset = StmtList; + StringRef LineData = OrigDwarf.getLineSection().Data; + DataExtractor LineExtractor(LineData, OrigDwarf.isLittleEndian(), + Unit.getOrigUnit().getAddressByteSize()); + LineTable.parse(LineExtractor, &OrigDwarf.getLineSection().Relocs, + &StmtOffset); + + // This vector is the output line table. + std::vector<DWARFDebugLine::Row> NewRows; + NewRows.reserve(LineTable.Rows.size()); + + // Current sequence of rows being extracted, before being inserted + // in NewRows. + std::vector<DWARFDebugLine::Row> Seq; + const auto &FunctionRanges = Unit.getFunctionRanges(); + auto InvalidRange = FunctionRanges.end(), CurrRange = InvalidRange; + + // FIXME: This logic is meant to generate exactly the same output as + // Darwin's classic dsynutil. There is a nicer way to implement this + // by simply putting all the relocated line info in NewRows and simply + // sorting NewRows before passing it to emitLineTableForUnit. This + // should be correct as sequences for a function should stay + // together in the sorted output. There are a few corner cases that + // look suspicious though, and that required to implement the logic + // this way. Revisit that once initial validation is finished. + + // Iterate over the object file line info and extract the sequences + // that correspond to linked functions. + for (auto &Row : LineTable.Rows) { + // Check wether we stepped out of the range. The range is + // half-open, but consider accept the end address of the range if + // it is marked as end_sequence in the input (because in that + // case, the relocation offset is accurate and that entry won't + // serve as the start of another function). + if (CurrRange == InvalidRange || Row.Address < CurrRange.start() || + Row.Address > CurrRange.stop() || + (Row.Address == CurrRange.stop() && !Row.EndSequence)) { + // We just stepped out of a known range. Insert a end_sequence + // corresponding to the end of the range. + uint64_t StopAddress = CurrRange != InvalidRange + ? CurrRange.stop() + CurrRange.value() + : -1ULL; + CurrRange = FunctionRanges.find(Row.Address); + bool CurrRangeValid = + CurrRange != InvalidRange && CurrRange.start() <= Row.Address; + if (!CurrRangeValid) { + CurrRange = InvalidRange; + if (StopAddress != -1ULL) { + // Try harder by looking in the DebugMapObject function + // ranges map. There are corner cases where this finds a + // valid entry. It's unclear if this is right or wrong, but + // for now do as dsymutil. + // FIXME: Understand exactly what cases this addresses and + // potentially remove it along with the Ranges map. + auto Range = Ranges.lower_bound(Row.Address); + if (Range != Ranges.begin() && Range != Ranges.end()) + --Range; + + if (Range != Ranges.end() && Range->first <= Row.Address && + Range->second.first >= Row.Address) { + StopAddress = Row.Address + Range->second.second; + } + } + } + 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; + insertLineSequence(Seq, NewRows); + } + + if (!CurrRangeValid) + continue; + } + + // Ignore empty sequences. + if (Row.EndSequence && Seq.empty()) + continue; + + // Relocate row address and add it to the current sequence. + Row.Address += CurrRange.value(); + Seq.emplace_back(Row); + + if (Row.EndSequence) + insertLineSequence(Seq, NewRows); + } + + // Finished extracting, now emit the line tables. + uint32_t PrologueEnd = StmtList + 10 + LineTable.Prologue.PrologueLength; + // FIXME: LLVM hardcodes it's prologue values. We just copy the + // prologue over and that works because we act as both producer and + // consumer. It would be nicer to have a real configurable line + // 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) + reportWarning("line table paramters mismatch. Cannot emit."); + else + Streamer->emitLineTableForUnit(LineData.slice(StmtList + 4, PrologueEnd), + LineTable.Prologue.MinInstLength, NewRows, + Unit.getOrigUnit().getAddressByteSize()); +} + +void DwarfLinker::emitAcceleratorEntriesForUnit(CompileUnit &Unit) { + Streamer->emitPubNamesForUnit(Unit); + Streamer->emitPubTypesForUnit(Unit); +} + +bool DwarfLinker::link(const DebugMap &Map) { + + if (Map.begin() == Map.end()) { + errs() << "Empty debug map.\n"; + return false; + } + + if (!createStreamer(Map.getTriple(), OutputFilename)) + return false; + + // Size of the DIEs (and headers) generated for the linked output. + uint64_t OutputDebugInfoSize = 0; + // A unique ID that identifies each compile unit. + unsigned UnitID = 0; + 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()); + continue; + } + + // Look for relocations that correspond to debug map entries. + if (!findValidRelocsInDebugInfo(*ErrOrObj, *Obj)) { + if (Options.Verbose) + outs() << "No valid relocations found. Skipping.\n"; + continue; + } + + // Setup access to the debug info. + 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. + 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()); + } + + // 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, + 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()); + } + + // Clean-up before starting working on the next object. + endDebugObject(); + } + + // Emit everything that's global. + if (!Options.NoOutput) { + Streamer->emitAbbrevs(Abbreviations); + Streamer->emitStrings(StringPool); + } + + return Options.NoOutput ? true : Streamer->finish(); +} +} + +bool linkDwarf(StringRef OutputFilename, const DebugMap &DM, + const LinkOptions &Options) { + DwarfLinker Linker(OutputFilename, Options); + return Linker.link(DM); +} } } diff --git a/tools/dsymutil/LLVMBuild.txt b/tools/dsymutil/LLVMBuild.txt index 24b9033c78c6..99b0b441c28e 100644 --- a/tools/dsymutil/LLVMBuild.txt +++ b/tools/dsymutil/LLVMBuild.txt @@ -19,4 +19,4 @@ type = Tool name = llvm-dsymutil parent = Tools -required_libraries = Object Support +required_libraries = AsmPrinter DebugInfoDWARF MC Object Support all-targets diff --git a/tools/dsymutil/MachODebugMapParser.cpp b/tools/dsymutil/MachODebugMapParser.cpp index 6b244fc369e9..bf64303b9eab 100644 --- a/tools/dsymutil/MachODebugMapParser.cpp +++ b/tools/dsymutil/MachODebugMapParser.cpp @@ -51,6 +51,10 @@ private: /// Element of the debug map corresponfing to the current object file. DebugMapObject *CurrentDebugMapObject; + /// Holds function info while function scope processing. + const char *CurrentFunctionName; + uint64_t CurrentFunctionAddress; + void switchToNewDebugMapObject(StringRef Filename); void resetParserState(); uint64_t getMainBinarySymbolAddress(StringRef Name); @@ -97,6 +101,13 @@ void MachODebugMapParser::switchToNewDebugMapObject(StringRef Filename) { CurrentDebugMapObject = &Result->addDebugMapObject(Path); } +static Triple getTriple(const object::MachOObjectFile &Obj) { + Triple TheTriple("unknown-unknown-unknown"); + TheTriple.setArch(Triple::ArchType(Obj.getArch())); + TheTriple.setObjectFormat(Triple::MachO); + return TheTriple; +} + /// 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. @@ -107,7 +118,7 @@ ErrorOr<std::unique_ptr<DebugMap>> MachODebugMapParser::parse() { const MachOObjectFile &MainBinary = *MainBinOrError; loadMainBinarySymbols(); - Result = make_unique<DebugMap>(); + Result = make_unique<DebugMap>(getTriple(MainBinary)); MainBinaryStrings = MainBinary.getStringTableData(); for (const SymbolRef &Symbol : MainBinary.symbols()) { const DataRefImpl &DRI = Symbol.getRawDataRefImpl(); @@ -142,6 +153,7 @@ void MachODebugMapParser::handleStabSymbolTableEntry(uint32_t StringIndex, if (!CurrentDebugMapObject) return; + uint32_t Size = 0; switch (Type) { case MachO::N_GSYM: // This is a global variable. We need to query the main binary @@ -152,11 +164,18 @@ void MachODebugMapParser::handleStabSymbolTableEntry(uint32_t StringIndex, return; break; case MachO::N_FUN: - // Functions are scopes in STABS. They have an end marker that we - // need to ignore. - if (Name[0] == '\0') + // Functions are scopes in STABS. They have an end marker that + // contains the function size. + if (Name[0] == '\0') { + Size = Value; + Value = CurrentFunctionAddress; + Name = CurrentFunctionName; + break; + } else { + CurrentFunctionName = Name; + CurrentFunctionAddress = Value; return; - break; + } case MachO::N_STSYM: break; default: @@ -167,7 +186,8 @@ 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 (!CurrentDebugMapObject->addSymbol(Name, ObjectSymIt->getValue(), Value, + Size)) return Warning(Twine("failed to insert symbol '") + Name + "' in the debug map."); } @@ -224,9 +244,8 @@ void MachODebugMapParser::loadMainBinarySymbols() { namespace llvm { namespace dsymutil { -llvm::ErrorOr<std::unique_ptr<DebugMap>> parseDebugMap(StringRef InputFile, - StringRef PrependPath, - bool Verbose) { +llvm::ErrorOr<std::unique_ptr<DebugMap>> +parseDebugMap(StringRef InputFile, StringRef PrependPath, bool Verbose) { MachODebugMapParser Parser(InputFile, PrependPath, Verbose); return Parser.parse(); } diff --git a/tools/dsymutil/Makefile b/tools/dsymutil/Makefile index 9eda7dcabc0a..c4365e0196b3 100644 --- a/tools/dsymutil/Makefile +++ b/tools/dsymutil/Makefile @@ -9,7 +9,7 @@ LEVEL := ../.. TOOLNAME := llvm-dsymutil -LINK_COMPONENTS := Object Support +LINK_COMPONENTS := all-targets AsmPrinter DebugInfoDWARF MC Object Support # This tool has no plugins, optimize startup time. TOOL_NO_EXPORTS := 1 diff --git a/tools/dsymutil/dsymutil.cpp b/tools/dsymutil/dsymutil.cpp index 2b4fcfe07008..4fc91b029f68 100644 --- a/tools/dsymutil/dsymutil.cpp +++ b/tools/dsymutil/dsymutil.cpp @@ -19,6 +19,7 @@ #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Support/TargetSelect.h" #include <string> using namespace llvm::dsymutil; @@ -29,6 +30,10 @@ using namespace llvm::cl; static opt<std::string> InputFile(Positional, desc("<input file>"), init("a.out")); +static opt<std::string> OutputFileOpt("o", desc("Specify the output file." + " default: <input file>.dwarf"), + value_desc("filename")); + static opt<std::string> OsoPrependPath("oso-prepend-path", desc("Specify a directory to prepend " "to the paths of object files."), @@ -36,6 +41,10 @@ static opt<std::string> OsoPrependPath("oso-prepend-path", static opt<bool> Verbose("v", desc("Verbosity level"), init(false)); +static opt<bool> NoOutput("no-output", desc("Do the link in memory, but do " + "not emit the result file."), + init(false)); + static opt<bool> ParseOnly("parse-only", desc("Only parse the debug map, do not actaully link " @@ -47,10 +56,19 @@ int main(int argc, char **argv) { llvm::sys::PrintStackTraceOnErrorSignal(); llvm::PrettyStackTraceProgram StackPrinter(argc, argv); llvm::llvm_shutdown_obj Shutdown; + LinkOptions Options; llvm::cl::ParseCommandLineOptions(argc, argv, "llvm dsymutil\n"); auto DebugMapPtrOrErr = parseDebugMap(InputFile, OsoPrependPath, Verbose); + Options.Verbose = Verbose; + Options.NoOutput = NoOutput; + + 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'; @@ -63,9 +81,15 @@ int main(int argc, char **argv) { if (ParseOnly) return 0; - std::string OutputBasename(InputFile); - if (OutputBasename == "-") - OutputBasename = "a.out"; + std::string OutputFile; + if (OutputFileOpt.empty()) { + if (InputFile == "-") + OutputFile = "a.out.dwarf"; + else + OutputFile = InputFile + ".dwarf"; + } else { + OutputFile = OutputFileOpt; + } - return !linkDwarf(OutputBasename + ".dwarf", **DebugMapPtrOrErr, Verbose); + return !linkDwarf(OutputFile, **DebugMapPtrOrErr, Options); } diff --git a/tools/dsymutil/dsymutil.h b/tools/dsymutil/dsymutil.h index 9203beaf6774..e9f7cd951878 100644 --- a/tools/dsymutil/dsymutil.h +++ b/tools/dsymutil/dsymutil.h @@ -23,6 +23,14 @@ namespace llvm { namespace dsymutil { + +struct LinkOptions { + bool Verbose; ///< Verbosity + bool NoOutput; ///< Skip emitting output + + 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>> @@ -33,7 +41,7 @@ parseDebugMap(StringRef InputFile, StringRef PrependPath = "", /// \p DM into a DwarfFile named \p OutputFilename. /// \returns false if the link failed. bool linkDwarf(StringRef OutputFilename, const DebugMap &DM, - bool Verbose = false); + const LinkOptions &Options); } } #endif // LLVM_TOOLS_DSYMUTIL_DSYMUTIL_H diff --git a/tools/gold/CMakeLists.txt b/tools/gold/CMakeLists.txt index a70905c84bf0..1a6169d65c2c 100644 --- a/tools/gold/CMakeLists.txt +++ b/tools/gold/CMakeLists.txt @@ -1,6 +1,3 @@ -set(LLVM_BINUTILS_INCDIR "" CACHE PATH - "PATH to binutils/include containing plugin-api.h for gold plugin.") - set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/gold.exports) if( LLVM_ENABLE_PIC AND LLVM_BINUTILS_INCDIR ) diff --git a/tools/gold/gold-plugin.cpp b/tools/gold/gold-plugin.cpp index a9909a721c1b..724e93cb8c74 100644 --- a/tools/gold/gold-plugin.cpp +++ b/tools/gold/gold-plugin.cpp @@ -15,26 +15,28 @@ #include "llvm/Config/config.h" // plugin-api.h requires HAVE_STDINT_H #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringSet.h" +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/Bitcode/ReaderWriter.h" #include "llvm/CodeGen/Analysis.h" #include "llvm/CodeGen/CommandFlags.h" +#include "llvm/IR/AutoUpgrade.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/LLVMContext.h" +#include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" #include "llvm/IR/Verifier.h" #include "llvm/Linker/Linker.h" #include "llvm/MC/SubtargetFeature.h" #include "llvm/Object/IRObjectFile.h" -#include "llvm/PassManager.h" -#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/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/Target/TargetLibraryInfo.h" #include "llvm/Transforms/IPO.h" #include "llvm/Transforms/IPO/PassManagerBuilder.h" #include "llvm/Transforms/Utils/GlobalStatus.h" @@ -89,6 +91,7 @@ namespace options { }; static bool generate_api_file = false; static OutputType TheOutputType = OT_NORMAL; + static unsigned OptLevel = 2; static std::string obj_path; static std::string extra_library_path; static std::string triple; @@ -122,6 +125,10 @@ namespace options { TheOutputType = OT_SAVE_TEMPS; } else if (opt == "disable-output") { TheOutputType = OT_DISABLE; + } 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"); + OptLevel = opt[1] - '0'; } else { // Save this option to pass to the code generator. // ParseCommandLineOptions() expects argv[0] to be program name. Lazily @@ -272,11 +279,11 @@ static bool shouldSkip(uint32_t Symflags) { } static void diagnosticHandler(const DiagnosticInfo &DI, void *Context) { - assert(DI.getSeverity() == DS_Error && "Only expecting errors"); - const auto &BDI = cast<BitcodeDiagnosticInfo>(DI); - std::error_code EC = BDI.getError(); - if (EC == BitcodeError::InvalidBitcodeSignature) - return; + if (const auto *BDI = dyn_cast<BitcodeDiagnosticInfo>(&DI)) { + std::error_code EC = BDI->getError(); + if (EC == BitcodeError::InvalidBitcodeSignature) + return; + } std::string ErrStorage; { @@ -284,8 +291,21 @@ static void diagnosticHandler(const DiagnosticInfo &DI, void *Context) { DiagnosticPrinterRawOStream DP(OS); DI.print(DP); } - message(LDPL_FATAL, "LLVM gold plugin has failed to create LTO module: %s", - ErrStorage.c_str()); + ld_plugin_level Level; + switch (DI.getSeverity()) { + case DS_Error: + message(LDPL_FATAL, "LLVM gold plugin has failed to create LTO module: %s", + ErrStorage.c_str()); + llvm_unreachable("Fatal doesn't return."); + case DS_Warning: + Level = LDPL_WARNING; + break; + case DS_Note: + case DS_Remark: + Level = LDPL_INFO; + break; + } + message(Level, "LLVM gold plugin: %s", ErrStorage.c_str()); } /// Called by gold to see whether this file is one that our plugin can handle. @@ -560,7 +580,7 @@ static void freeSymName(ld_plugin_symbol &Sym) { static std::unique_ptr<Module> getModuleForFile(LLVMContext &Context, claimed_file &F, - off_t Filesize, raw_fd_ostream *ApiFile, + ld_plugin_input_file &Info, raw_fd_ostream *ApiFile, StringSet<> &Internalize, StringSet<> &Maybe) { if (get_symbols(F.handle, F.syms.size(), &F.syms[0]) != LDPS_OK) @@ -570,7 +590,8 @@ getModuleForFile(LLVMContext &Context, claimed_file &F, if (get_view(F.handle, &View) != LDPS_OK) message(LDPL_FATAL, "Failed to get a view of file"); - MemoryBufferRef BufferRef(StringRef((const char *)View, Filesize), ""); + MemoryBufferRef BufferRef(StringRef((const char *)View, Info.filesize), + Info.name); ErrorOr<std::unique_ptr<object::IRObjectFile>> ObjOrErr = object::IRObjectFile::create(BufferRef, Context); @@ -582,6 +603,9 @@ getModuleForFile(LLVMContext &Context, claimed_file &F, Module &M = Obj.getModule(); + M.materializeMetadata(); + UpgradeDebugInfo(M); + SmallPtrSet<GlobalValue *, 8> Used; collectUsedGlobalVariables(M, Used, /*CompilerUsed*/ false); @@ -692,15 +716,21 @@ getModuleForFile(LLVMContext &Context, claimed_file &F, } static void runLTOPasses(Module &M, TargetMachine &TM) { - PassManager passes; + if (const DataLayout *DL = TM.getDataLayout()) + M.setDataLayout(*DL); + + legacy::PassManager passes; + passes.add(createTargetTransformInfoWrapperPass(TM.getTargetIRAnalysis())); + PassManagerBuilder PMB; - PMB.LibraryInfo = new TargetLibraryInfo(Triple(TM.getTargetTriple())); + PMB.LibraryInfo = new TargetLibraryInfoImpl(Triple(TM.getTargetTriple())); PMB.Inliner = createFunctionInliningPass(); PMB.VerifyInput = true; PMB.VerifyOutput = true; PMB.LoopVectorize = true; PMB.SLPVectorize = true; - PMB.populateLTOPassManager(passes, &TM); + PMB.OptLevel = options::OptLevel; + PMB.populateLTOPassManager(passes); passes.run(M); } @@ -709,7 +739,7 @@ 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); + WriteBitcodeToFile(&M, OS, /* ShouldPreserveUseListOrder */ true); } static void codegen(Module &M) { @@ -730,17 +760,31 @@ static void codegen(Module &M) { Features.AddFeature(A); TargetOptions Options = InitTargetOptionsFromCodeGenFlags(); + CodeGenOpt::Level CGOptLevel; + switch (options::OptLevel) { + case 0: + CGOptLevel = CodeGenOpt::None; + break; + case 1: + CGOptLevel = CodeGenOpt::Less; + break; + case 2: + CGOptLevel = CodeGenOpt::Default; + break; + case 3: + CGOptLevel = CodeGenOpt::Aggressive; + break; + } std::unique_ptr<TargetMachine> TM(TheTarget->createTargetMachine( TripleStr, options::mcpu, Features.getString(), Options, RelocationModel, - CodeModel::Default, CodeGenOpt::Aggressive)); + CodeModel::Default, CGOptLevel)); runLTOPasses(M, *TM); if (options::TheOutputType == options::OT_SAVE_TEMPS) saveBCFile(output_name + ".opt.bc", M); - PassManager CodeGenPasses; - CodeGenPasses.add(new DataLayoutPass()); + legacy::PassManager CodeGenPasses; SmallString<128> Filename; int FD; @@ -760,9 +804,8 @@ static void codegen(Module &M) { { raw_fd_ostream OS(FD, true); - formatted_raw_ostream FOS(OS); - if (TM->addPassesToEmitFile(CodeGenPasses, FOS, + if (TM->addPassesToEmitFile(CodeGenPasses, OS, TargetMachine::CGFT_ObjectFile)) message(LDPL_FATAL, "Failed to setup codegen"); CodeGenPasses.run(M); @@ -785,6 +828,8 @@ static ld_plugin_status allSymbolsReadHook(raw_fd_ostream *ApiFile) { return LDPS_OK; LLVMContext Context; + Context.setDiagnosticHandler(diagnosticHandler, nullptr, true); + std::unique_ptr<Module> Combined(new Module("ld-temp.o", Context)); Linker L(Combined.get()); @@ -797,8 +842,7 @@ static ld_plugin_status allSymbolsReadHook(raw_fd_ostream *ApiFile) { 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.filesize, ApiFile, - Internalize, Maybe); + getModuleForFile(Context, F, File, ApiFile, Internalize, Maybe); if (!options::triple.empty()) M->setTargetTriple(options::triple.c_str()); else if (M->getTargetTriple().empty()) { @@ -865,8 +909,13 @@ static ld_plugin_status all_symbols_read_hook(void) { llvm_shutdown(); if (options::TheOutputType == options::OT_BC_ONLY || - options::TheOutputType == options::OT_DISABLE) + options::TheOutputType == options::OT_DISABLE) { + if (options::TheOutputType == options::OT_DISABLE) + // Remove the output file here since ld.bfd creates the output file + // early. + sys::fs::remove(output_name); exit(0); + } return Ret; } diff --git a/tools/llc/CMakeLists.txt b/tools/llc/CMakeLists.txt index 393d64c0ef08..dcbcf9da6128 100644 --- a/tools/llc/CMakeLists.txt +++ b/tools/llc/CMakeLists.txt @@ -1,5 +1,6 @@ set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} + Analysis AsmPrinter CodeGen Core @@ -8,7 +9,6 @@ set(LLVM_LINK_COMPONENTS ScalarOpts SelectionDAG Support - Target ) # Support plugins. @@ -17,4 +17,4 @@ set(LLVM_NO_DEAD_STRIP 1) add_llvm_tool(llc llc.cpp ) -set_target_properties(llc PROPERTIES ENABLE_EXPORTS 1) +export_executable_symbols(llc) diff --git a/tools/llc/llc.cpp b/tools/llc/llc.cpp index 35f965b2a300..0977418e0231 100644 --- a/tools/llc/llc.cpp +++ b/tools/llc/llc.cpp @@ -14,18 +14,21 @@ //===----------------------------------------------------------------------===// +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Triple.h" +#include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/CodeGen/CommandFlags.h" #include "llvm/CodeGen/LinkAllAsmWriterComponents.h" #include "llvm/CodeGen/LinkAllCodegenComponents.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/IRPrintingPasses.h" #include "llvm/IR/LLVMContext.h" +#include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" +#include "llvm/IR/Verifier.h" #include "llvm/IRReader/IRReader.h" #include "llvm/MC/SubtargetFeature.h" #include "llvm/Pass.h" -#include "llvm/PassManager.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/FileSystem.h" @@ -39,7 +42,6 @@ #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/ToolOutputFile.h" -#include "llvm/Target/TargetLibraryInfo.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Target/TargetSubtargetInfo.h" #include <memory> @@ -210,12 +212,6 @@ static int compileModule(char **argv, LLVMContext &Context) { bool SkipModule = MCPU == "help" || (!MAttrs.empty() && MAttrs.front() == "help"); - // If user asked for the 'native' CPU, autodetect here. If autodection fails, - // this will set the CPU to an empty string which tells the target to - // pick a basic default. - if (MCPU == "native") - MCPU = sys::getHostCPUName(); - // If user just wants to list available options, skip module loading if (!SkipModule) { M = parseIRFile(InputFilename, Err, Context); @@ -224,6 +220,14 @@ static int compileModule(char **argv, LLVMContext &Context) { return 1; } + // Verify module immediately to catch problems before doInitialization() is + // called on any passes. + if (!NoVerify && verifyModule(*M, &errs())) { + errs() << argv[0] << ": " << InputFilename + << ": error: input module is broken!\n"; + return 1; + } + // If we are supposed to override the target triple, do so now. if (!TargetTriple.empty()) M->setTargetTriple(Triple::normalize(TargetTriple)); @@ -244,14 +248,7 @@ static int compileModule(char **argv, LLVMContext &Context) { return 1; } - // Package up features to be passed to target/subtarget - std::string FeaturesStr; - if (MAttrs.size()) { - SubtargetFeatures Features; - for (unsigned i = 0; i != MAttrs.size(); ++i) - Features.AddFeature(MAttrs[i]); - FeaturesStr = Features.getString(); - } + std::string CPUStr = getCPUStr(), FeaturesStr = getFeaturesStr(); CodeGenOpt::Level OLvl = CodeGenOpt::Default; switch (OptLevel) { @@ -272,8 +269,9 @@ static int compileModule(char **argv, LLVMContext &Context) { Options.MCOptions.AsmVerbose = AsmVerbose; std::unique_ptr<TargetMachine> Target( - TheTarget->createTargetMachine(TheTriple.getTriple(), MCPU, FeaturesStr, + TheTarget->createTargetMachine(TheTriple.getTriple(), CPUStr, FeaturesStr, Options, RelocModel, CMModel, OLvl)); + assert(Target && "Could not allocate target machine!"); // If we don't have a module then just exit now. We do this down @@ -283,9 +281,8 @@ static int compileModule(char **argv, LLVMContext &Context) { return 0; assert(M && "Should have exited if we didn't have a module!"); - - if (GenerateSoftFloatCalls) - FloatABIForCalls = FloatABI::Soft; + if (FloatABIForCalls != FloatABI::Default) + Options.FloatABIType = FloatABIForCalls; // Figure out where we are going to send the output. std::unique_ptr<tool_output_file> Out = @@ -293,18 +290,23 @@ static int compileModule(char **argv, LLVMContext &Context) { if (!Out) return 1; // Build up all of the passes that we want to do to the module. - PassManager PM; + legacy::PassManager PM; // Add an appropriate TargetLibraryInfo pass for the module's triple. - TargetLibraryInfo *TLI = new TargetLibraryInfo(TheTriple); + TargetLibraryInfoImpl TLII(Triple(M->getTargetTriple())); + + // The -disable-simplify-libcalls flag actually disables all builtin optzns. if (DisableSimplifyLibCalls) - TLI->disableAllFunctions(); - PM.add(TLI); + TLII.disableAllFunctions(); + PM.add(new TargetLibraryInfoWrapperPass(TLII)); // Add the target data from the target machine, if it exists, or the module. - if (const DataLayout *DL = Target->getSubtargetImpl()->getDataLayout()) - M->setDataLayout(DL); - PM.add(new DataLayoutPass()); + if (const DataLayout *DL = Target->getDataLayout()) + M->setDataLayout(*DL); + + // Override function attributes based on CPUStr, FeaturesStr, and command line + // flags. + setFunctionAttributes(CPUStr, FeaturesStr, *M); if (RelaxAll.getNumOccurrences() > 0 && FileType != TargetMachine::CGFT_ObjectFile) @@ -312,7 +314,13 @@ static int compileModule(char **argv, LLVMContext &Context) { << ": warning: ignoring -mc-relax-all because filetype != obj"; { - formatted_raw_ostream FOS(Out->os()); + 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); + OS = BOS.get(); + } AnalysisID StartAfterID = nullptr; AnalysisID StopAfterID = nullptr; @@ -335,8 +343,8 @@ static int compileModule(char **argv, LLVMContext &Context) { } // Ask the target to add backend passes as necessary. - if (Target->addPassesToEmitFile(PM, FOS, FileType, NoVerify, - StartAfterID, StopAfterID)) { + if (Target->addPassesToEmitFile(PM, *OS, FileType, NoVerify, StartAfterID, + StopAfterID)) { errs() << argv[0] << ": target does not support generation of this" << " file type!\n"; return 1; diff --git a/tools/lli/CMakeLists.txt b/tools/lli/CMakeLists.txt index 51f55791ea47..844b99464cb7 100644 --- a/tools/lli/CMakeLists.txt +++ b/tools/lli/CMakeLists.txt @@ -10,9 +10,11 @@ set(LLVM_LINK_COMPONENTS MC MCJIT Object + OrcJIT RuntimeDyld SelectionDAG Support + TransformUtils native ) @@ -26,7 +28,7 @@ endif( LLVM_USE_OPROFILE ) if( LLVM_USE_INTEL_JITEVENTS ) set(LLVM_LINK_COMPONENTS ${LLVM_LINK_COMPONENTS} - DebugInfo + DebugInfoDWARF IntelJITEvents Object ) @@ -34,8 +36,9 @@ endif( LLVM_USE_INTEL_JITEVENTS ) add_llvm_tool(lli lli.cpp + OrcLazyJIT.cpp RemoteMemoryManager.cpp RemoteTarget.cpp RemoteTargetExternal.cpp ) -set_target_properties(lli PROPERTIES ENABLE_EXPORTS 1) +export_executable_symbols(lli) diff --git a/tools/lli/LLVMBuild.txt b/tools/lli/LLVMBuild.txt index 4c14c47bd0d5..580ba9a4ded6 100644 --- a/tools/lli/LLVMBuild.txt +++ b/tools/lli/LLVMBuild.txt @@ -22,4 +22,4 @@ subdirectories = ChildTarget type = Tool name = lli parent = Tools -required_libraries = AsmParser BitReader IRReader Instrumentation Interpreter MCJIT NativeCodeGen SelectionDAG Native +required_libraries = AsmParser BitReader IRReader Instrumentation Interpreter MCJIT NativeCodeGen SelectionDAG TransformUtils Native diff --git a/tools/lli/Makefile b/tools/lli/Makefile index 94d6f061c946..70d8c80e5c0a 100644 --- a/tools/lli/Makefile +++ b/tools/lli/Makefile @@ -14,12 +14,12 @@ PARALLEL_DIRS := ChildTarget include $(LEVEL)/Makefile.config -LINK_COMPONENTS := mcjit instrumentation interpreter nativecodegen bitreader asmparser irreader selectiondag native +LINK_COMPONENTS := mcjit orcjit instrumentation interpreter nativecodegen bitreader asmparser irreader selectiondag native # If Intel JIT Events support is confiured, link against the LLVM Intel JIT # Events interface library ifeq ($(USE_INTEL_JITEVENTS), 1) - LINK_COMPONENTS += debuginfo inteljitevents object + LINK_COMPONENTS += debuginfodwarf inteljitevents object endif # If oprofile support is confiured, link against the LLVM oprofile interface diff --git a/tools/lli/OrcLazyJIT.cpp b/tools/lli/OrcLazyJIT.cpp new file mode 100644 index 000000000000..bda5d6dcd26c --- /dev/null +++ b/tools/lli/OrcLazyJIT.cpp @@ -0,0 +1,148 @@ +//===------ OrcLazyJIT.cpp - Basic Orc-based JIT for lazy execution -------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "OrcLazyJIT.h" +#include "llvm/ExecutionEngine/Orc/OrcTargetSupport.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/DynamicLibrary.h" +#include <cstdio> +#include <system_error> + +using namespace llvm; + +namespace { + + enum class DumpKind { NoDump, DumpFuncsToStdOut, DumpModsToStdErr, + DumpModsToDisk }; + + cl::opt<DumpKind> OrcDumpKind("orc-lazy-debug", + cl::desc("Debug dumping for the orc-lazy JIT."), + cl::init(DumpKind::NoDump), + cl::values( + clEnumValN(DumpKind::NoDump, "no-dump", + "Don't dump anything."), + clEnumValN(DumpKind::DumpFuncsToStdOut, + "funcs-to-stdout", + "Dump function names to stdout."), + clEnumValN(DumpKind::DumpModsToStdErr, + "mods-to-stderr", + "Dump modules to stderr."), + clEnumValN(DumpKind::DumpModsToDisk, + "mods-to-disk", + "Dump modules to the current " + "working directory. (WARNING: " + "will overwrite existing files)."), + clEnumValEnd)); +} + +OrcLazyJIT::CallbackManagerBuilder +OrcLazyJIT::createCallbackManagerBuilder(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); + }; + } + } +} + +OrcLazyJIT::TransformFtor OrcLazyJIT::createDebugDumper() { + + switch (OrcDumpKind) { + case DumpKind::NoDump: + return [](std::unique_ptr<Module> M) { return M; }; + + case DumpKind::DumpFuncsToStdOut: + return [](std::unique_ptr<Module> M) { + printf("[ "); + + for (const auto &F : *M) { + if (F.isDeclaration()) + continue; + + if (F.hasName()) { + std::string Name(F.getName()); + printf("%s ", Name.c_str()); + } else + printf("<anon> "); + } + + printf("]\n"); + return M; + }; + + case DumpKind::DumpModsToStdErr: + return [](std::unique_ptr<Module> M) { + dbgs() << "----- Module Start -----\n" << *M + << "----- Module End -----\n"; + + return M; + }; + + case DumpKind::DumpModsToDisk: + return [](std::unique_ptr<Module> M) { + std::error_code EC; + raw_fd_ostream Out(M->getModuleIdentifier() + ".ll", EC, + sys::fs::F_Text); + if (EC) { + errs() << "Couldn't open " << M->getModuleIdentifier() + << " for dumping.\nError:" << EC.message() << "\n"; + exit(1); + } + Out << *M; + return M; + }; + } + llvm_unreachable("Unknown DumpKind"); +} + +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)) { + errs() << "Error loading program symbols.\n"; + return 1; + } + + // Grab a target machine and try to build a factory function for the + // target-specific Orc callback manager. + auto TM = std::unique_ptr<TargetMachine>(EngineBuilder().selectTarget()); + auto &Context = getGlobalContext(); + auto CallbackMgrBuilder = + OrcLazyJIT::createCallbackManagerBuilder(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) { + errs() << "No callback manager available for target '" + << TM->getTargetTriple() << "'.\n"; + return 1; + } + + // Everything looks good. Build the JIT. + OrcLazyJIT J(std::move(TM), Context, CallbackMgrBuilder); + + // Add the module, look up main and run it. + auto MainHandle = J.addModule(std::move(M)); + auto MainSym = J.findSymbolIn(MainHandle, "main"); + + if (!MainSym) { + errs() << "Could not find main function.\n"; + return 1; + } + + typedef int (*MainFnPtr)(int, char*[]); + auto Main = OrcLazyJIT::fromTargetAddress<MainFnPtr>(MainSym.getAddress()); + return Main(ArgC, ArgV); +} diff --git a/tools/lli/OrcLazyJIT.h b/tools/lli/OrcLazyJIT.h new file mode 100644 index 000000000000..bff2eca26a5f --- /dev/null +++ b/tools/lli/OrcLazyJIT.h @@ -0,0 +1,164 @@ +//===--- OrcLazyJIT.h - Basic Orc-based JIT for lazy execution --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Simple Orc-based JIT. Uses the compile-on-demand layer to break up and +// lazily compile modules. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLI_ORCLAZYJIT_H +#define LLVM_TOOLS_LLI_ORCLAZYJIT_H + +#include "llvm/ADT/Triple.h" +#include "llvm/ExecutionEngine/Orc/CompileOnDemandLayer.h" +#include "llvm/ExecutionEngine/Orc/CompileUtils.h" +#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" +#include "llvm/ExecutionEngine/Orc/IRCompileLayer.h" +#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::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::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)), + Mang(this->TM->getDataLayout()), + ObjectLayer(), + CompileLayer(ObjectLayer, orc::SimpleCompiler(*this->TM)), + IRDumpLayer(CompileLayer, createDebugDumper()), + CCMgr(BuildCallbackMgr(IRDumpLayer, CCMgrMemMgr, Context)), + CODLayer(IRDumpLayer, *CCMgr), + CXXRuntimeOverrides([this](const std::string &S) { return mangle(S); }) {} + + ~OrcLazyJIT() { + // Run any destructors registered with __cxa_atexit. + CXXRuntimeOverrides.runDestructors(); + // Run any IR destructors. + for (auto &DtorRunner : IRStaticDestructorRunners) + DtorRunner.runViaLayer(CODLayer); + } + + template <typename PtrTy> + static PtrTy fromTargetAddress(orc::TargetAddress Addr) { + return reinterpret_cast<PtrTy>(static_cast<uintptr_t>(Addr)); + } + + 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()); + + // Record the static constructors and destructors. We have to do this before + // we hand over ownership of the module to the JIT. + std::vector<std::string> CtorNames, DtorNames; + for (auto Ctor : orc::getConstructors(*M)) + CtorNames.push_back(mangle(Ctor.Func->getName())); + for (auto Dtor : orc::getDestructors(*M)) + DtorNames.push_back(mangle(Dtor.Func->getName())); + + // Symbol resolution order: + // 1) Search the JIT symbols. + // 2) Check for C++ runtime overrides. + // 3) Search the host process (LLI)'s symbol table. + auto Resolver = + orc::createLambdaResolver( + [this](const std::string &Name) { + + if (auto Sym = CODLayer.findSymbol(Name, true)) + return RuntimeDyld::SymbolInfo(Sym.getAddress(), Sym.getFlags()); + + if (auto Sym = CXXRuntimeOverrides.searchOverrides(Name)) + return Sym; + + if (auto Addr = RTDyldMemoryManager::getSymbolAddressInProcess(Name)) + return RuntimeDyld::SymbolInfo(Addr, JITSymbolFlags::Exported); + + return RuntimeDyld::SymbolInfo(nullptr); + }, + [](const std::string &Name) { return RuntimeDyld::SymbolInfo(nullptr); } + ); + + // Add the module to the JIT. + std::vector<std::unique_ptr<Module>> S; + S.push_back(std::move(M)); + auto H = CODLayer.addModuleSet(std::move(S), nullptr, std::move(Resolver)); + + // Run the static constructors, and save the static destructor runner for + // execution when the JIT is torn down. + orc::CtorDtorRunner<CODLayerT> CtorRunner(std::move(CtorNames), H); + CtorRunner.runViaLayer(CODLayer); + + IRStaticDestructorRunners.push_back( + orc::CtorDtorRunner<CODLayerT>(std::move(DtorNames), H)); + + return H; + } + + orc::JITSymbol findSymbol(const std::string &Name) { + return CODLayer.findSymbol(mangle(Name), true); + } + + orc::JITSymbol findSymbolIn(ModuleHandleT H, const std::string &Name) { + return CODLayer.findSymbolIn(H, mangle(Name), true); + } + +private: + + std::string mangle(const std::string &Name) { + std::string MangledName; + { + raw_string_ostream MangledNameStream(MangledName); + Mang.getNameWithPrefix(MangledNameStream, Name); + } + return MangledName; + } + + static TransformFtor createDebugDumper(); + + std::unique_ptr<TargetMachine> TM; + Mangler Mang; + SectionMemoryManager CCMgrMemMgr; + + ObjLayerT ObjectLayer; + CompileLayerT CompileLayer; + IRDumpLayerT IRDumpLayer; + std::unique_ptr<CompileCallbackMgr> CCMgr; + CODLayerT CODLayer; + + orc::LocalCXXRuntimeOverrides CXXRuntimeOverrides; + std::vector<orc::CtorDtorRunner<CODLayerT>> IRStaticDestructorRunners; +}; + +int runOrcLazyJIT(std::unique_ptr<Module> M, int ArgC, char* ArgV[]); + +} // end namespace llvm + +#endif diff --git a/tools/lli/RemoteMemoryManager.cpp b/tools/lli/RemoteMemoryManager.cpp index 47da8fb60c98..0a16210d2ea5 100644 --- a/tools/lli/RemoteMemoryManager.cpp +++ b/tools/lli/RemoteMemoryManager.cpp @@ -16,6 +16,7 @@ #include "llvm/ExecutionEngine/ExecutionEngine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" using namespace llvm; diff --git a/tools/lli/RemoteMemoryManager.h b/tools/lli/RemoteMemoryManager.h index 895bcdac4d14..5733fa53f3e4 100644 --- a/tools/lli/RemoteMemoryManager.h +++ b/tools/lli/RemoteMemoryManager.h @@ -64,7 +64,7 @@ private: public: RemoteMemoryManager() : Target(nullptr) {} - virtual ~RemoteMemoryManager(); + ~RemoteMemoryManager() override; uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, diff --git a/tools/lli/RemoteTargetExternal.h b/tools/lli/RemoteTargetExternal.h index bb621f5c50f0..afe8570ff49f 100644 --- a/tools/lli/RemoteTargetExternal.h +++ b/tools/lli/RemoteTargetExternal.h @@ -106,7 +106,7 @@ public: void stop() override; RemoteTargetExternal(std::string &Name) : RemoteTarget(), ChildName(Name) {} - virtual ~RemoteTargetExternal() {} + ~RemoteTargetExternal() override {} private: std::string ChildName; diff --git a/tools/lli/lli.cpp b/tools/lli/lli.cpp index 730911b07c65..6916d1609b83 100644 --- a/tools/lli/lli.cpp +++ b/tools/lli/lli.cpp @@ -14,6 +14,7 @@ //===----------------------------------------------------------------------===// #include "llvm/IR/LLVMContext.h" +#include "OrcLazyJIT.h" #include "RemoteMemoryManager.h" #include "RemoteTarget.h" #include "RemoteTargetExternal.h" @@ -25,6 +26,7 @@ #include "llvm/ExecutionEngine/JITEventListener.h" #include "llvm/ExecutionEngine/MCJIT.h" #include "llvm/ExecutionEngine/ObjectCache.h" +#include "llvm/ExecutionEngine/OrcMCJITReplacement.h" #include "llvm/ExecutionEngine/SectionMemoryManager.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Module.h" @@ -41,6 +43,7 @@ #include "llvm/Support/MathExtras.h" #include "llvm/Support/Memory.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" #include "llvm/Support/PluginLoader.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Process.h" @@ -64,6 +67,9 @@ using namespace llvm; #define DEBUG_TYPE "lli" namespace { + + enum class JITKind { MCJIT, OrcMCJITReplacement, OrcLazy }; + cl::opt<std::string> InputFile(cl::desc("<input bitcode>"), cl::Positional, cl::init("-")); @@ -74,6 +80,20 @@ namespace { cl::desc("Force interpretation: disable JIT"), cl::init(false)); + cl::opt<JITKind> UseJITKind("jit-kind", + cl::desc("Choose underlying JIT kind."), + cl::init(JITKind::MCJIT), + cl::values( + clEnumValN(JITKind::MCJIT, "mcjit", + "MCJIT"), + clEnumValN(JITKind::OrcMCJITReplacement, + "orc-mcjit", + "Orc-based MCJIT replacement"), + clEnumValN(JITKind::OrcLazy, + "orc-lazy", + "Orc-based lazy JIT."), + clEnumValEnd)); + // The MCJIT supports building for a target address space separate from // the JIT compilation process. Use a forked process and a copying // memory manager with IPC to execute using this functionality. @@ -215,23 +235,6 @@ namespace { clEnumValN(FloatABI::Hard, "hard", "Hard float ABI (uses FP registers)"), clEnumValEnd)); - cl::opt<bool> -// In debug builds, make this default to true. -#ifdef NDEBUG -#define EMIT_DEBUG false -#else -#define EMIT_DEBUG true -#endif - EmitJitDebugInfo("jit-emit-debug", - cl::desc("Emit debug information to debugger"), - cl::init(EMIT_DEBUG)); -#undef EMIT_DEBUG - - static cl::opt<bool> - EmitJitDebugInfoToDisk("jit-emit-debug-to-disk", - cl::Hidden, - cl::desc("Emit debug info objfiles to disk"), - cl::init(false)); } //===----------------------------------------------------------------------===// @@ -251,7 +254,7 @@ public: this->CacheDir[this->CacheDir.size() - 1] != '/') this->CacheDir += '/'; } - virtual ~LLIObjectCache() {} + ~LLIObjectCache() override {} void notifyObjectCompiled(const Module *M, MemoryBufferRef Obj) override { const std::string ModuleID = M->getModuleIdentifier(); @@ -395,6 +398,9 @@ int main(int argc, char **argv, char * const *envp) { return 1; } + if (UseJITKind == JITKind::OrcLazy) + return runOrcLazyJIT(std::move(Owner), argc, argv); + if (EnableCacheManager) { std::string CacheName("file:"); CacheName.append(InputFile); @@ -421,6 +427,7 @@ int main(int argc, char **argv, char * const *envp) { builder.setEngineKind(ForceInterpreter ? EngineKind::Interpreter : EngineKind::JIT); + builder.setUseOrcMCJITReplacement(UseJITKind == JITKind::OrcMCJITReplacement); // If we are supposed to override the target triple, do so now. if (!TargetTriple.empty()) @@ -458,17 +465,8 @@ int main(int argc, char **argv, char * const *envp) { builder.setOptLevel(OLvl); TargetOptions Options; - Options.UseSoftFloat = GenerateSoftFloatCalls; if (FloatABIForCalls != FloatABI::Default) Options.FloatABIType = FloatABIForCalls; - if (GenerateSoftFloatCalls) - FloatABIForCalls = FloatABI::Soft; - - // Remote target execution doesn't handle EH or debug registration. - if (!RemoteMCJIT) { - Options.JITEmitDebugInfo = EmitJitDebugInfo; - Options.JITEmitDebugInfoToDisk = EmitJitDebugInfoToDisk; - } builder.setTargetOptions(Options); @@ -556,7 +554,7 @@ int main(int argc, char **argv, char * const *envp) { // If the user specifically requested an argv[0] to pass into the program, // do it now. if (!FakeArgv0.empty()) { - InputFile = FakeArgv0; + InputFile = static_cast<std::string>(FakeArgv0); } else { // Otherwise, if there is a .bc suffix on the executable strip it off, it // might confuse the program. @@ -588,7 +586,7 @@ int main(int argc, char **argv, char * const *envp) { // function later on to make an explicit call, so get the function now. Constant *Exit = Mod->getOrInsertFunction("exit", Type::getVoidTy(Context), Type::getInt32Ty(Context), - NULL); + nullptr); // Run static constructors. if (!ForceInterpreter) { diff --git a/tools/llvm-ar/llvm-ar.cpp b/tools/llvm-ar/llvm-ar.cpp index f72762ac174a..f813465daafa 100644 --- a/tools/llvm-ar/llvm-ar.cpp +++ b/tools/llvm-ar/llvm-ar.cpp @@ -24,6 +24,7 @@ #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/TargetSelect.h" @@ -92,7 +93,6 @@ static cl::extrahelp MoreHelp( " [a] - put file(s) after [relpos]\n" " [b] - put file(s) before [relpos] (same as [i])\n" " [i] - put file(s) before [relpos] (same as [b])\n" - " [N] - use instance [count] of name\n" " [o] - preserve original dates\n" " [s] - create an archive index (cf. ranlib)\n" " [S] - do not build a symbol table\n" diff --git a/tools/llvm-as/llvm-as.cpp b/tools/llvm-as/llvm-as.cpp index 5ccf505923f2..4455d24fb60d 100644 --- a/tools/llvm-as/llvm-as.cpp +++ b/tools/llvm-as/llvm-as.cpp @@ -51,20 +51,19 @@ static cl::opt<bool> DisableVerify("disable-verify", cl::Hidden, cl::desc("Do not run verifier on input LLVM (dangerous!)")); +static cl::opt<bool> PreserveBitcodeUseListOrder( + "preserve-bc-uselistorder", + cl::desc("Preserve use-list order when writing LLVM bitcode."), + cl::init(true), cl::Hidden); + static void WriteOutputFile(const Module *M) { // Infer the output filename if needed. if (OutputFilename.empty()) { if (InputFilename == "-") { OutputFilename = "-"; } else { - std::string IFN = InputFilename; - int Len = IFN.length(); - if (IFN[Len-3] == '.' && IFN[Len-2] == 'l' && IFN[Len-1] == 'l') { - // Source ends in .ll - OutputFilename = std::string(IFN.begin(), IFN.end()-3); - } else { - OutputFilename = IFN; // Append a .bc to it - } + StringRef IFN = InputFilename; + OutputFilename = (IFN.endswith(".ll") ? IFN.drop_back(3) : IFN).str(); OutputFilename += ".bc"; } } @@ -78,7 +77,7 @@ static void WriteOutputFile(const Module *M) { } if (Force || !CheckBitcodeOutputToConsole(Out->os(), true)) - WriteBitcodeToFile(M, Out->os()); + WriteBitcodeToFile(M, Out->os(), PreserveBitcodeUseListOrder); // Declare success. Out->keep(); diff --git a/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp b/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp index 396e03036989..58b02be7b92f 100644 --- a/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp +++ b/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp @@ -66,6 +66,10 @@ static cl::opt<std::string> BlockInfoFilename("block-info", cl::desc("Use the BLOCK_INFO from the given file")); +static cl::opt<bool> + ShowBinaryBlobs("show-binary-blobs", + cl::desc("Print binary blobs using hex escapes")); + namespace { /// CurStreamTypeType - A type for CurStreamType @@ -222,8 +226,10 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID, case bitc::FUNC_CODE_INST_BINOP: return "INST_BINOP"; case bitc::FUNC_CODE_INST_CAST: return "INST_CAST"; - case bitc::FUNC_CODE_INST_GEP: return "INST_GEP"; - case bitc::FUNC_CODE_INST_INBOUNDS_GEP: return "INST_INBOUNDS_GEP"; + case bitc::FUNC_CODE_INST_GEP_OLD: + return "INST_GEP_OLD"; + case bitc::FUNC_CODE_INST_INBOUNDS_GEP_OLD: + return "INST_INBOUNDS_GEP_OLD"; case bitc::FUNC_CODE_INST_SELECT: return "INST_SELECT"; case bitc::FUNC_CODE_INST_EXTRACTELT: return "INST_EXTRACTELT"; case bitc::FUNC_CODE_INST_INSERTELT: return "INST_INSERTELT"; @@ -248,6 +254,8 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID, case bitc::FUNC_CODE_DEBUG_LOC_AGAIN: return "DEBUG_LOC_AGAIN"; case bitc::FUNC_CODE_INST_CALL: return "INST_CALL"; case bitc::FUNC_CODE_DEBUG_LOC: return "DEBUG_LOC"; + case bitc::FUNC_CODE_INST_GEP: + return "INST_GEP"; } case bitc::VALUE_SYMTAB_BLOCK_ID: switch (CodeID) { @@ -456,17 +464,22 @@ static bool ParseBlock(BitstreamCursor &Stream, unsigned BlockID, if (Blob.data()) { outs() << " blob data = "; - bool BlobIsPrintable = true; - for (unsigned i = 0, e = Blob.size(); i != e; ++i) - if (!isprint(static_cast<unsigned char>(Blob[i]))) { - BlobIsPrintable = false; - break; - } - - if (BlobIsPrintable) - outs() << "'" << Blob << "'"; - else - outs() << "unprintable, " << Blob.size() << " bytes."; + if (ShowBinaryBlobs) { + outs() << "'"; + outs().write_escaped(Blob, /*hex=*/true) << "'"; + } else { + bool BlobIsPrintable = true; + for (unsigned i = 0, e = Blob.size(); i != e; ++i) + if (!isprint(static_cast<unsigned char>(Blob[i]))) { + BlobIsPrintable = false; + break; + } + + if (BlobIsPrintable) + outs() << "'" << Blob << "'"; + else + outs() << "unprintable, " << Blob.size() << " bytes."; + } } outs() << "\n"; diff --git a/tools/llvm-config/CMakeLists.txt b/tools/llvm-config/CMakeLists.txt index 50c84e6c3d08..d2ef45a16815 100644 --- a/tools/llvm-config/CMakeLists.txt +++ b/tools/llvm-config/CMakeLists.txt @@ -44,7 +44,7 @@ if(CMAKE_CROSSCOMPILING) WORKING_DIRECTORY ${LLVM_NATIVE_BUILD} COMMENT "Building native llvm-config...") add_custom_target(${project}NativeLLVMConfig DEPENDS ${${project}_LLVM_CONFIG_EXE}) - add_dependencies(${project}NativeLLVMConfig ConfigureNativeLLVM) + add_dependencies(${project}NativeLLVMConfig CONFIGURE_LLVM_NATIVE) add_dependencies(llvm-config ${project}NativeLLVMConfig) endif(CMAKE_CROSSCOMPILING) diff --git a/tools/llvm-config/llvm-config.cpp b/tools/llvm-config/llvm-config.cpp index 224035ac497b..879b9ab0945b 100644 --- a/tools/llvm-config/llvm-config.cpp +++ b/tools/llvm-config/llvm-config.cpp @@ -91,9 +91,9 @@ static void VisitComponent(StringRef Name, /// are required to link the given components. /// \param IncludeNonInstalled - Whether non-installed components should be /// reported. -void ComputeLibsForComponents(const std::vector<StringRef> &Components, - std::vector<StringRef> &RequiredLibs, - bool IncludeNonInstalled) { +static void ComputeLibsForComponents(const std::vector<StringRef> &Components, + std::vector<StringRef> &RequiredLibs, + bool IncludeNonInstalled) { std::set<AvailableComponent*> VisitedComponents; // Build a map of component names to information. @@ -126,7 +126,7 @@ void ComputeLibsForComponents(const std::vector<StringRef> &Components, /* *** */ -void usage() { +static void usage() { errs() << "\ usage: llvm-config <OPTION>... [<COMPONENT>...]\n\ \n\ diff --git a/tools/llvm-cov/CMakeLists.txt b/tools/llvm-cov/CMakeLists.txt index b2d2b897ec95..193218a6639f 100644 --- a/tools/llvm-cov/CMakeLists.txt +++ b/tools/llvm-cov/CMakeLists.txt @@ -6,7 +6,6 @@ add_llvm_tool(llvm-cov CodeCoverage.cpp CoverageFilters.cpp CoverageReport.cpp - CoverageSummary.cpp CoverageSummaryInfo.cpp SourceCoverageView.cpp TestingSupport.cpp diff --git a/tools/llvm-cov/CodeCoverage.cpp b/tools/llvm-cov/CodeCoverage.cpp index 7cee4d4b3f2e..f85f3b13b675 100644 --- a/tools/llvm-cov/CodeCoverage.cpp +++ b/tools/llvm-cov/CodeCoverage.cpp @@ -16,13 +16,12 @@ #include "RenderingSupport.h" #include "CoverageFilters.h" #include "CoverageReport.h" -#include "CoverageSummary.h" #include "CoverageViewOptions.h" #include "SourceCoverageView.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.h" #include "llvm/ProfileData/CoverageMapping.h" -#include "llvm/ProfileData/CoverageMappingReader.h" #include "llvm/ProfileData/InstrProfReader.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" @@ -30,6 +29,7 @@ #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/Path.h" #include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Process.h" #include "llvm/Support/Signals.h" #include <functional> #include <system_error> @@ -89,6 +89,7 @@ public: LoadedSourceFiles; bool CompareFilenamesOnly; StringMap<std::string> RemappedFilenames; + llvm::Triple::ArchType CoverageArch; }; } @@ -194,8 +195,22 @@ CodeCoverageTool::createSourceFileView(StringRef SourceFile, return View; } +static bool modifiedTimeGT(StringRef LHS, StringRef RHS) { + sys::fs::file_status Status; + if (sys::fs::status(LHS, Status)) + return false; + auto LHSTime = Status.getLastModificationTime(); + if (sys::fs::status(RHS, Status)) + return false; + auto RHSTime = Status.getLastModificationTime(); + return LHSTime > RHSTime; +} + std::unique_ptr<CoverageMapping> CodeCoverageTool::load() { - auto CoverageOrErr = CoverageMapping::load(ObjectFilename, PGOFilename); + if (modifiedTimeGT(ObjectFilename, PGOFilename)) + errs() << "warning: profile data may be out of date - object is newer\n"; + auto CoverageOrErr = CoverageMapping::load(ObjectFilename, PGOFilename, + CoverageArch); if (std::error_code EC = CoverageOrErr.getError()) { colored_ostream(errs(), raw_ostream::RED) << "error: Failed to load coverage: " << EC.message(); @@ -244,6 +259,9 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { cl::desc( "File with the profile data obtained after an instrumented run")); + cl::opt<std::string> Arch( + "arch", cl::desc("architecture of the coverage mapping binary")); + cl::opt<bool> DebugDump("dump", cl::Optional, cl::desc("Show internal debug dump")); @@ -289,11 +307,19 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { "greater than the given threshold"), cl::cat(FilteringCategory)); + cl::opt<cl::boolOrDefault> UseColor( + "use-color", cl::desc("Emit colored output (default=autodetect)"), + cl::init(cl::BOU_UNSET)); + auto commandLineParser = [&, this](int argc, const char **argv) -> int { cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); ViewOpts.Debug = DebugDump; CompareFilenamesOnly = FilenameEquivalence; + ViewOpts.Colors = UseColor == cl::BOU_UNSET + ? sys::Process::StandardOutHasColors() + : UseColor == cl::BOU_TRUE; + // Create the function filters if (!NameFilters.empty() || !NameRegexFilters.empty()) { auto NameFilterer = new CoverageFilters; @@ -324,12 +350,23 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { Filters.push_back(std::unique_ptr<CoverageFilter>(StatFilterer)); } - for (const auto &File : InputSourceFiles) { - SmallString<128> Path(File); - if (std::error_code EC = sys::fs::make_absolute(Path)) { - errs() << "error: " << File << ": " << EC.message(); + if (Arch.empty()) + CoverageArch = llvm::Triple::ArchType::UnknownArch; + else { + CoverageArch = Triple(Arch).getArch(); + if (CoverageArch == llvm::Triple::ArchType::UnknownArch) { + errs() << "error: Unknown architecture: " << Arch << "\n"; return 1; } + } + + for (const auto &File : InputSourceFiles) { + SmallString<128> Path(File); + if (!CompareFilenamesOnly) + if (std::error_code EC = sys::fs::make_absolute(Path)) { + errs() << "error: " << File << ": " << EC.message(); + return 1; + } SourceFiles.push_back(Path.str()); } return 0; @@ -373,15 +410,10 @@ int CodeCoverageTool::show(int argc, const char **argv, cl::desc("Show function instantiations"), cl::cat(ViewCategory)); - cl::opt<bool> NoColors("no-colors", cl::Optional, - cl::desc("Don't show text colors"), cl::init(false), - cl::cat(ViewCategory)); - auto Err = commandLineParser(argc, argv); if (Err) return Err; - ViewOpts.Colors = !NoColors; ViewOpts.ShowLineNumbers = true; ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 || !ShowRegions || ShowBestLineRegionsCounts; @@ -447,28 +479,19 @@ int CodeCoverageTool::show(int argc, const char **argv, int CodeCoverageTool::report(int argc, const char **argv, CommandLineParserType commandLineParser) { - cl::opt<bool> NoColors("no-colors", cl::Optional, - cl::desc("Don't show text colors"), cl::init(false)); - auto Err = commandLineParser(argc, argv); if (Err) return Err; - ViewOpts.Colors = !NoColors; - auto Coverage = load(); if (!Coverage) return 1; - CoverageSummary Summarizer; - Summarizer.createSummaries(*Coverage); - CoverageReport Report(ViewOpts, Summarizer); - if (SourceFiles.empty() && Filters.empty()) { + CoverageReport Report(ViewOpts, std::move(Coverage)); + if (SourceFiles.empty()) Report.renderFileReports(llvm::outs()); - return 0; - } - - Report.renderFunctionReports(llvm::outs()); + else + Report.renderFunctionReports(SourceFiles, llvm::outs()); return 0; } diff --git a/tools/llvm-cov/CoverageReport.cpp b/tools/llvm-cov/CoverageReport.cpp index 6ae6ba55eb9c..497c2f88f268 100644 --- a/tools/llvm-cov/CoverageReport.cpp +++ b/tools/llvm-cov/CoverageReport.cpp @@ -12,7 +12,6 @@ //===----------------------------------------------------------------------===// #include "CoverageReport.h" -#include "CoverageSummary.h" #include "RenderingSupport.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" @@ -156,14 +155,15 @@ void CoverageReport::render(const FunctionCoverageSummary &Function, OS << "\n"; } -void CoverageReport::renderFunctionReports(raw_ostream &OS) { +void CoverageReport::renderFunctionReports(ArrayRef<std::string> Files, + raw_ostream &OS) { bool isFirst = true; - for (const auto &File : Summary.getFileSummaries()) { + for (StringRef Filename : Files) { if (isFirst) isFirst = false; else OS << "\n"; - OS << "File '" << File.Name << "':\n"; + OS << "File '" << Filename << "':\n"; OS << column("Name", FunctionReportColumns[0]) << column("Regions", FunctionReportColumns[1], Column::RightAlignment) << column("Miss", FunctionReportColumns[2], Column::RightAlignment) @@ -174,13 +174,19 @@ void CoverageReport::renderFunctionReports(raw_ostream &OS) { OS << "\n"; renderDivider(FunctionReportColumns, OS); OS << "\n"; - for (const auto &Function : File.FunctionSummaries) + FunctionCoverageSummary Totals("TOTAL"); + for (const auto &F : Coverage->getCoveredFunctions(Filename)) { + FunctionCoverageSummary Function = FunctionCoverageSummary::get(F); + ++Totals.ExecutionCount; + Totals.RegionCoverage += Function.RegionCoverage; + Totals.LineCoverage += Function.LineCoverage; render(Function, OS); - renderDivider(FunctionReportColumns, OS); - OS << "\n"; - render(FunctionCoverageSummary("TOTAL", /*ExecutionCount=*/0, - File.RegionCoverage, File.LineCoverage), - OS); + } + if (Totals.ExecutionCount) { + renderDivider(FunctionReportColumns, OS); + OS << "\n"; + render(Totals, OS); + } } } @@ -194,9 +200,17 @@ void CoverageReport::renderFileReports(raw_ostream &OS) { << "\n"; renderDivider(FileReportColumns, OS); OS << "\n"; - for (const auto &File : Summary.getFileSummaries()) - render(File, OS); + FileCoverageSummary Totals("TOTAL"); + for (StringRef Filename : Coverage->getUniqueSourceFiles()) { + FileCoverageSummary Summary(Filename); + for (const auto &F : Coverage->getCoveredFunctions(Filename)) { + FunctionCoverageSummary Function = FunctionCoverageSummary::get(F); + Summary.addFunction(Function); + Totals.addFunction(Function); + } + render(Summary, OS); + } renderDivider(FileReportColumns, OS); OS << "\n"; - render(Summary.getCombinedFileSummaries(), OS); + render(Totals, OS); } diff --git a/tools/llvm-cov/CoverageReport.h b/tools/llvm-cov/CoverageReport.h index d18611740ae6..7ec3df90b8f9 100644 --- a/tools/llvm-cov/CoverageReport.h +++ b/tools/llvm-cov/CoverageReport.h @@ -14,7 +14,7 @@ #ifndef LLVM_COV_COVERAGEREPORT_H #define LLVM_COV_COVERAGEREPORT_H -#include "CoverageSummary.h" +#include "CoverageSummaryInfo.h" #include "CoverageViewOptions.h" namespace llvm { @@ -22,16 +22,17 @@ namespace llvm { /// \brief Displays the code coverage report. class CoverageReport { const CoverageViewOptions &Options; - CoverageSummary &Summary; + std::unique_ptr<coverage::CoverageMapping> Coverage; void render(const FileCoverageSummary &File, raw_ostream &OS); void render(const FunctionCoverageSummary &Function, raw_ostream &OS); public: - CoverageReport(const CoverageViewOptions &Options, CoverageSummary &Summary) - : Options(Options), Summary(Summary) {} + CoverageReport(const CoverageViewOptions &Options, + std::unique_ptr<coverage::CoverageMapping> Coverage) + : Options(Options), Coverage(std::move(Coverage)) {} - void renderFunctionReports(raw_ostream &OS); + void renderFunctionReports(ArrayRef<std::string> Files, raw_ostream &OS); void renderFileReports(raw_ostream &OS); }; diff --git a/tools/llvm-cov/CoverageSummary.cpp b/tools/llvm-cov/CoverageSummary.cpp deleted file mode 100644 index 059c8c857e45..000000000000 --- a/tools/llvm-cov/CoverageSummary.cpp +++ /dev/null @@ -1,64 +0,0 @@ -//===- CoverageSummary.cpp - Code coverage summary ------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This class implements data management and rendering for the code coverage -// summaries of all files and functions. -// -//===----------------------------------------------------------------------===// - -#include "CoverageSummary.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Format.h" - -using namespace llvm; - -unsigned CoverageSummary::getFileID(StringRef Filename) { - for (unsigned I = 0, E = Filenames.size(); I < E; ++I) { - if (sys::fs::equivalent(Filenames[I], Filename)) - return I; - } - Filenames.push_back(Filename); - return Filenames.size() - 1; -} - -void -CoverageSummary::createSummaries(const coverage::CoverageMapping &Coverage) { - for (StringRef Filename : Coverage.getUniqueSourceFiles()) { - size_t PrevSize = FunctionSummaries.size(); - for (const auto &F : Coverage.getCoveredFunctions(Filename)) - FunctionSummaries.push_back(FunctionCoverageSummary::get(F)); - size_t Count = FunctionSummaries.size() - PrevSize; - if (Count == 0) - continue; - FileSummaries.push_back(FileCoverageSummary::get( - Filename, makeArrayRef(FunctionSummaries.data() + PrevSize, Count))); - } -} - -FileCoverageSummary CoverageSummary::getCombinedFileSummaries() { - size_t NumRegions = 0, CoveredRegions = 0; - size_t NumLines = 0, NonCodeLines = 0, CoveredLines = 0; - size_t NumFunctionsExecuted = 0, NumFunctions = 0; - for (const auto &File : FileSummaries) { - NumRegions += File.RegionCoverage.NumRegions; - CoveredRegions += File.RegionCoverage.Covered; - - NumLines += File.LineCoverage.NumLines; - NonCodeLines += File.LineCoverage.NonCodeLines; - CoveredLines += File.LineCoverage.Covered; - - NumFunctionsExecuted += File.FunctionCoverage.Executed; - NumFunctions += File.FunctionCoverage.NumFunctions; - } - return FileCoverageSummary( - "TOTAL", RegionCoverageInfo(CoveredRegions, NumRegions), - LineCoverageInfo(CoveredLines, NonCodeLines, NumLines), - FunctionCoverageInfo(NumFunctionsExecuted, NumFunctions), - None); -} diff --git a/tools/llvm-cov/CoverageSummary.h b/tools/llvm-cov/CoverageSummary.h deleted file mode 100644 index 9dbebde949e5..000000000000 --- a/tools/llvm-cov/CoverageSummary.h +++ /dev/null @@ -1,45 +0,0 @@ -//===- CoverageSummary.h - Code coverage summary --------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This class implements data management and rendering for the code coverage -// summaries of all files and functions. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_COV_COVERAGESUMMARY_H -#define LLVM_COV_COVERAGESUMMARY_H - -#include "CoverageSummaryInfo.h" -#include <vector> - -namespace llvm { - -/// \brief Manager for the function and file code coverage summaries. -class CoverageSummary { - std::vector<StringRef> Filenames; - std::vector<FunctionCoverageSummary> FunctionSummaries; - std::vector<std::pair<unsigned, unsigned>> FunctionSummariesFileIDs; - std::vector<FileCoverageSummary> FileSummaries; - - unsigned getFileID(StringRef Filename); - -public: - void createSummaries(const coverage::CoverageMapping &Coverage); - - ArrayRef<FileCoverageSummary> getFileSummaries() { return FileSummaries; } - - FileCoverageSummary getCombinedFileSummaries(); - - void render(const FunctionCoverageSummary &Summary, raw_ostream &OS); - - void render(raw_ostream &OS); -}; -} - -#endif // LLVM_COV_COVERAGESUMMARY_H diff --git a/tools/llvm-cov/CoverageSummaryInfo.cpp b/tools/llvm-cov/CoverageSummaryInfo.cpp index dd78ace86050..de8975097e49 100644 --- a/tools/llvm-cov/CoverageSummaryInfo.cpp +++ b/tools/llvm-cov/CoverageSummaryInfo.cpp @@ -69,28 +69,3 @@ FunctionCoverageSummary::get(const coverage::FunctionRecord &Function) { RegionCoverageInfo(CoveredRegions, NumCodeRegions), LineCoverageInfo(CoveredLines, 0, NumLines)); } - -FileCoverageSummary -FileCoverageSummary::get(StringRef Name, - ArrayRef<FunctionCoverageSummary> FunctionSummaries) { - size_t NumRegions = 0, CoveredRegions = 0; - size_t NumLines = 0, NonCodeLines = 0, CoveredLines = 0; - size_t NumFunctionsExecuted = 0; - for (const auto &Func : FunctionSummaries) { - CoveredRegions += Func.RegionCoverage.Covered; - NumRegions += Func.RegionCoverage.NumRegions; - - CoveredLines += Func.LineCoverage.Covered; - NonCodeLines += Func.LineCoverage.NonCodeLines; - NumLines += Func.LineCoverage.NumLines; - - if (Func.ExecutionCount != 0) - ++NumFunctionsExecuted; - } - - return FileCoverageSummary( - Name, RegionCoverageInfo(CoveredRegions, NumRegions), - LineCoverageInfo(CoveredLines, NonCodeLines, NumLines), - FunctionCoverageInfo(NumFunctionsExecuted, FunctionSummaries.size()), - FunctionSummaries); -} diff --git a/tools/llvm-cov/CoverageSummaryInfo.h b/tools/llvm-cov/CoverageSummaryInfo.h index 0036032ab399..c393b00d32a4 100644 --- a/tools/llvm-cov/CoverageSummaryInfo.h +++ b/tools/llvm-cov/CoverageSummaryInfo.h @@ -31,10 +31,19 @@ struct RegionCoverageInfo { /// \brief The total number of regions in a function/file. size_t NumRegions; + RegionCoverageInfo() : Covered(0), NotCovered(0), NumRegions(0) {} + RegionCoverageInfo(size_t Covered, size_t NumRegions) : Covered(Covered), NotCovered(NumRegions - Covered), NumRegions(NumRegions) {} + RegionCoverageInfo &operator+=(const RegionCoverageInfo &RHS) { + Covered += RHS.Covered; + NotCovered += RHS.NotCovered; + NumRegions += RHS.NumRegions; + return *this; + } + bool isFullyCovered() const { return Covered == NumRegions; } double getPercentCovered() const { @@ -56,10 +65,21 @@ struct LineCoverageInfo { /// \brief The total number of lines in a function/file. size_t NumLines; + LineCoverageInfo() + : Covered(0), NotCovered(0), NonCodeLines(0), NumLines(0) {} + LineCoverageInfo(size_t Covered, size_t NumNonCodeLines, size_t NumLines) : Covered(Covered), NotCovered(NumLines - NumNonCodeLines - Covered), NonCodeLines(NumNonCodeLines), NumLines(NumLines) {} + LineCoverageInfo &operator+=(const LineCoverageInfo &RHS) { + Covered += RHS.Covered; + NotCovered += RHS.NotCovered; + NonCodeLines += RHS.NonCodeLines; + NumLines += RHS.NumLines; + return *this; + } + bool isFullyCovered() const { return Covered == (NumLines - NonCodeLines); } double getPercentCovered() const { @@ -75,9 +95,17 @@ struct FunctionCoverageInfo { /// \brief The total number of functions in this file. size_t NumFunctions; + FunctionCoverageInfo() : Executed(0), NumFunctions(0) {} + FunctionCoverageInfo(size_t Executed, size_t NumFunctions) : Executed(Executed), NumFunctions(NumFunctions) {} + void addFunction(bool Covered) { + if (Covered) + ++Executed; + ++NumFunctions; + } + bool isFullyCovered() const { return Executed == NumFunctions; } double getPercentCovered() const { @@ -92,6 +120,8 @@ struct FunctionCoverageSummary { RegionCoverageInfo RegionCoverage; LineCoverageInfo LineCoverage; + FunctionCoverageSummary(StringRef Name) : Name(Name), ExecutionCount(0) {} + FunctionCoverageSummary(StringRef Name, uint64_t ExecutionCount, const RegionCoverageInfo &RegionCoverage, const LineCoverageInfo &LineCoverage) @@ -111,21 +141,14 @@ struct FileCoverageSummary { RegionCoverageInfo RegionCoverage; LineCoverageInfo LineCoverage; FunctionCoverageInfo FunctionCoverage; - /// \brief The summary of every function - /// in this file. - ArrayRef<FunctionCoverageSummary> FunctionSummaries; - - FileCoverageSummary(StringRef Name, const RegionCoverageInfo &RegionCoverage, - const LineCoverageInfo &LineCoverage, - const FunctionCoverageInfo &FunctionCoverage, - ArrayRef<FunctionCoverageSummary> FunctionSummaries) - : Name(Name), RegionCoverage(RegionCoverage), LineCoverage(LineCoverage), - FunctionCoverage(FunctionCoverage), - FunctionSummaries(FunctionSummaries) {} - - /// \brief Compute the code coverage summary for a file. - static FileCoverageSummary - get(StringRef Name, ArrayRef<FunctionCoverageSummary> FunctionSummaries); + + FileCoverageSummary(StringRef Name) : Name(Name) {} + + void addFunction(const FunctionCoverageSummary &Function) { + RegionCoverage += Function.RegionCoverage; + LineCoverage += Function.LineCoverage; + FunctionCoverage.addFunction(/*Covered=*/Function.ExecutionCount > 0); + } }; } // namespace llvm diff --git a/tools/llvm-cov/RenderingSupport.h b/tools/llvm-cov/RenderingSupport.h index 0271329997dc..3ef155d3fd4b 100644 --- a/tools/llvm-cov/RenderingSupport.h +++ b/tools/llvm-cov/RenderingSupport.h @@ -18,7 +18,7 @@ namespace llvm { /// \brief A helper class that resets the output stream's color if needed /// when destroyed. class ColoredRawOstream { - ColoredRawOstream(const ColoredRawOstream &OS) LLVM_DELETED_FUNCTION; + ColoredRawOstream(const ColoredRawOstream &OS) = delete; public: raw_ostream &OS; diff --git a/tools/llvm-cov/SourceCoverageView.cpp b/tools/llvm-cov/SourceCoverageView.cpp index 015099c7d02b..58c8a6795294 100644 --- a/tools/llvm-cov/SourceCoverageView.cpp +++ b/tools/llvm-cov/SourceCoverageView.cpp @@ -14,6 +14,7 @@ #include "SourceCoverageView.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/Support/LineIterator.h" using namespace llvm; @@ -77,6 +78,22 @@ void SourceCoverageView::renderViewDivider(unsigned Level, unsigned Length, OS << "-"; } +/// Format a count using engineering notation with 3 significant digits. +static std::string formatCount(uint64_t N) { + std::string Number = utostr(N); + int Len = Number.size(); + if (Len <= 3) + return Number; + int IntLen = Len % 3 == 0 ? 3 : Len % 3; + std::string Result(Number.data(), IntLen); + if (IntLen != 3) { + Result.push_back('.'); + Result += Number.substr(IntLen, 3 - IntLen); + } + Result.push_back(" kMGTPEZY"[(Len - 1) / 3]); + return Result; +} + void SourceCoverageView::renderLineCoverageColumn(raw_ostream &OS, const LineCoverageInfo &Line) { @@ -84,17 +101,11 @@ SourceCoverageView::renderLineCoverageColumn(raw_ostream &OS, OS.indent(LineCoverageColumnWidth) << '|'; return; } - SmallString<32> Buffer; - raw_svector_ostream BufferOS(Buffer); - BufferOS << Line.ExecutionCount; - auto Str = BufferOS.str(); - // Trim - Str = Str.substr(0, std::min(Str.size(), (size_t)LineCoverageColumnWidth)); - // Align to the right - OS.indent(LineCoverageColumnWidth - Str.size()); + std::string C = formatCount(Line.ExecutionCount); + OS.indent(LineCoverageColumnWidth - C.size()); colored_ostream(OS, raw_ostream::MAGENTA, Line.hasMultipleRegions() && Options.Colors) - << Str; + << C; OS << '|'; } @@ -111,9 +122,6 @@ void SourceCoverageView::renderLineNumberColumn(raw_ostream &OS, void SourceCoverageView::renderRegionMarkers( raw_ostream &OS, ArrayRef<const coverage::CoverageSegment *> Segments) { - SmallString<32> Buffer; - raw_svector_ostream BufferOS(Buffer); - unsigned PrevColumn = 1; for (const auto *S : Segments) { if (!S->IsRegionEntry) @@ -122,20 +130,16 @@ void SourceCoverageView::renderRegionMarkers( if (S->Col > PrevColumn) OS.indent(S->Col - PrevColumn); PrevColumn = S->Col + 1; - BufferOS << S->Count; - StringRef Str = BufferOS.str(); - // Trim the execution count - Str = Str.substr(0, std::min(Str.size(), (size_t)7)); - PrevColumn += Str.size(); - OS << '^' << Str; - Buffer.clear(); + std::string C = formatCount(S->Count); + PrevColumn += C.size(); + OS << '^' << C; } OS << "\n"; if (Options.Debug) for (const auto *S : Segments) - errs() << "Marker at " << S->Line << ":" << S->Col << " = " << S->Count - << (S->IsRegionEntry ? "\n" : " (pop)\n"); + errs() << "Marker at " << S->Line << ":" << S->Col << " = " + << formatCount(S->Count) << (S->IsRegionEntry ? "\n" : " (pop)\n"); } void SourceCoverageView::render(raw_ostream &OS, bool WholeFile, diff --git a/tools/llvm-cov/SourceCoverageView.h b/tools/llvm-cov/SourceCoverageView.h index d92a7486d9d3..9e6fe5f35001 100644 --- a/tools/llvm-cov/SourceCoverageView.h +++ b/tools/llvm-cov/SourceCoverageView.h @@ -90,15 +90,14 @@ private: bool hasMultipleRegions() const { return RegionCount > 1; } void addRegionStartCount(uint64_t Count) { - Mapped = true; - ExecutionCount = Count; + // The max of all region starts is the most interesting value. + addRegionCount(RegionCount ? std::max(ExecutionCount, Count) : Count); ++RegionCount; } void addRegionCount(uint64_t Count) { Mapped = true; - if (!RegionCount) - ExecutionCount = Count; + ExecutionCount = Count; } }; diff --git a/tools/llvm-cov/gcov.cpp b/tools/llvm-cov/gcov.cpp index 12011cea7f26..4377a50b5566 100644 --- a/tools/llvm-cov/gcov.cpp +++ b/tools/llvm-cov/gcov.cpp @@ -23,9 +23,10 @@ #include <system_error> using namespace llvm; -void reportCoverage(StringRef SourceFile, StringRef ObjectDir, - const std::string &InputGCNO, const std::string &InputGCDA, - bool DumpGCOV, const GCOVOptions &Options) { +static void reportCoverage(StringRef SourceFile, StringRef ObjectDir, + const std::string &InputGCNO, + const std::string &InputGCDA, bool DumpGCOV, + const GCOVOptions &Options) { SmallString<128> CoverageFileStem(ObjectDir); if (CoverageFileStem.empty()) { // If no directory was specified with -o, look next to the source file. @@ -80,7 +81,7 @@ void reportCoverage(StringRef SourceFile, StringRef ObjectDir, FileInfo FI(Options); GF.collectLineCounts(FI); - FI.print(SourceFile, GCNO, GCDA); + FI.print(llvm::outs(), SourceFile, GCNO, GCDA); } int gcovMain(int argc, const char *argv[]) { diff --git a/tools/llvm-cov/llvm-cov.cpp b/tools/llvm-cov/llvm-cov.cpp index 86ec26dbd188..bf66f583a528 100644 --- a/tools/llvm-cov/llvm-cov.cpp +++ b/tools/llvm-cov/llvm-cov.cpp @@ -14,6 +14,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" #include "llvm/Support/raw_ostream.h" #include <string> @@ -32,9 +33,13 @@ int convertForTestingMain(int argc, const char *argv[]); int gcovMain(int argc, const char *argv[]); /// \brief Top level help. -int helpMain(int argc, const char *argv[]) { - errs() << "OVERVIEW: LLVM code coverage tool\n\n" - << "USAGE: llvm-cov {gcov|report|show}\n"; +static int helpMain(int argc, const char *argv[]) { + errs() << "Usage: llvm-cov {gcov|report|show} [OPTION]...\n\n" + << "Shows code coverage information.\n\n" + << "Subcommands:\n" + << " gcov: Work with the gcov format.\n" + << " show: Annotate source files using instrprof style coverage.\n" + << " report: Summarize instrprof style coverage information.\n"; return 0; } @@ -61,18 +66,13 @@ int main(int argc, const char **argv) { } } - // Give a warning and fall back to gcov - errs().changeColor(raw_ostream::RED); - errs() << "warning:"; - // Assume that argv[1] wasn't a command when it stats with a '-' or is a - // filename (i.e. contains a '.') - if (argc > 1 && !StringRef(argv[1]).startswith("-") && - StringRef(argv[1]).find(".") == StringRef::npos) - errs() << " Unrecognized command '" << argv[1] << "'."; - errs() << " Using the gcov compatible mode " - "(this behaviour may be dropped in the future)."; - errs().resetColor(); - errs() << "\n"; - - return gcovMain(argc, argv); + if (argc > 1) { + if (sys::Process::StandardErrHasColors()) + errs().changeColor(raw_ostream::RED); + errs() << "Unrecognized command: " << argv[1] << ".\n\n"; + if (sys::Process::StandardErrHasColors()) + errs().resetColor(); + } + helpMain(argc, argv); + return 1; } diff --git a/tools/llvm-vtabledump/CMakeLists.txt b/tools/llvm-cxxdump/CMakeLists.txt index 4fe205b6efd4..ada886e88873 100644 --- a/tools/llvm-vtabledump/CMakeLists.txt +++ b/tools/llvm-cxxdump/CMakeLists.txt @@ -4,7 +4,7 @@ set(LLVM_LINK_COMPONENTS Support ) -add_llvm_tool(llvm-vtabledump - llvm-vtabledump.cpp +add_llvm_tool(llvm-cxxdump + llvm-cxxdump.cpp Error.cpp ) diff --git a/tools/llvm-vtabledump/Error.cpp b/tools/llvm-cxxdump/Error.cpp index c5de8951636d..16fed96e4834 100644 --- a/tools/llvm-vtabledump/Error.cpp +++ b/tools/llvm-cxxdump/Error.cpp @@ -1,4 +1,4 @@ -//===- Error.cpp - system_error extensions for llvm-vtabledump --*- C++ -*-===// +//===- Error.cxx - system_error extensions for llvm-cxxdump -----*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// // -// This defines a new error_category for the llvm-vtabledump tool. +// This defines a new error_category for the llvm-cxxdump tool. // //===----------------------------------------------------------------------===// @@ -17,27 +17,27 @@ using namespace llvm; namespace { -class vtabledump_error_category : public std::error_category { +class cxxdump_error_category : public std::error_category { public: - const char *name() const LLVM_NOEXCEPT override { return "llvm.vtabledump"; } + const char *name() const LLVM_NOEXCEPT override { return "llvm.cxxdump"; } std::string message(int ev) const override { - switch (static_cast<vtabledump_error>(ev)) { - case vtabledump_error::success: + switch (static_cast<cxxdump_error>(ev)) { + case cxxdump_error::success: return "Success"; - case vtabledump_error::file_not_found: + case cxxdump_error::file_not_found: return "No such file."; - case vtabledump_error::unrecognized_file_format: + case cxxdump_error::unrecognized_file_format: return "Unrecognized file type."; } llvm_unreachable( - "An enumerator of vtabledump_error does not have a message defined."); + "An enumerator of cxxdump_error does not have a message defined."); } }; } // namespace namespace llvm { -const std::error_category &vtabledump_category() { - static vtabledump_error_category o; +const std::error_category &cxxdump_category() { + static cxxdump_error_category o; return o; } } // namespace llvm diff --git a/tools/llvm-vtabledump/Error.h b/tools/llvm-cxxdump/Error.h index fd8bb18994df..7caf6d6447c9 100644 --- a/tools/llvm-vtabledump/Error.h +++ b/tools/llvm-cxxdump/Error.h @@ -1,4 +1,4 @@ -//===- Error.h - system_error extensions for llvm-vtabledump ----*- C++ -*-===// +//===- Error.h - system_error extensions for llvm-cxxdump -------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -7,33 +7,33 @@ // //===----------------------------------------------------------------------===// // -// This declares a new error_category for the llvm-vtabledump tool. +// This declares a new error_category for the llvm-cxxdump tool. // //===----------------------------------------------------------------------===// -#ifndef LLVM_TOOLS_LLVM_VTABLEDUMP_ERROR_H -#define LLVM_TOOLS_LLVM_VTABLEDUMP_ERROR_H +#ifndef LLVM_TOOLS_LLVM_CXXDUMP_ERROR_H +#define LLVM_TOOLS_LLVM_CXXDUMP_ERROR_H #include <system_error> namespace llvm { -const std::error_category &vtabledump_category(); +const std::error_category &cxxdump_category(); -enum class vtabledump_error { +enum class cxxdump_error { success = 0, file_not_found, unrecognized_file_format, }; -inline std::error_code make_error_code(vtabledump_error e) { - return std::error_code(static_cast<int>(e), vtabledump_category()); +inline std::error_code make_error_code(cxxdump_error e) { + return std::error_code(static_cast<int>(e), cxxdump_category()); } } // namespace llvm namespace std { template <> -struct is_error_code_enum<llvm::vtabledump_error> : std::true_type {}; +struct is_error_code_enum<llvm::cxxdump_error> : std::true_type {}; } #endif diff --git a/tools/llvm-vtabledump/LLVMBuild.txt b/tools/llvm-cxxdump/LLVMBuild.txt index 6a3cbfff4c13..4e088157799f 100644 --- a/tools/llvm-vtabledump/LLVMBuild.txt +++ b/tools/llvm-cxxdump/LLVMBuild.txt @@ -1,4 +1,4 @@ -;===- ./tools/llvm-vtabledump/LLVMBuild.txt --------------------*- Conf -*--===; +;===- ./tools/llvm-cxxdump/LLVMBuild.txt -----------------------*- Conf -*--===; ; ; The LLVM Compiler Infrastructure ; @@ -17,6 +17,6 @@ [component_0] type = Tool -name = llvm-vtabledump +name = llvm-cxxdump parent = Tools required_libraries = all-targets BitReader Object diff --git a/tools/llvm-vtabledump/Makefile b/tools/llvm-cxxdump/Makefile index 596c64c67b6b..02e8e5f90108 100644 --- a/tools/llvm-vtabledump/Makefile +++ b/tools/llvm-cxxdump/Makefile @@ -1,4 +1,4 @@ -##===- tools/llvm-vtabledump/Makefile ----------------------*- Makefile -*-===## +##===- tools/llvm-cxxdump/Makefile -------------------------*- Makefile -*-===## # # The LLVM Compiler Infrastructure # @@ -8,7 +8,7 @@ ##===----------------------------------------------------------------------===## LEVEL := ../.. -TOOLNAME := llvm-vtabledump +TOOLNAME := llvm-cxxdump LINK_COMPONENTS := bitreader object all-targets # This tool has no plugins, optimize startup time. diff --git a/tools/llvm-vtabledump/llvm-vtabledump.cpp b/tools/llvm-cxxdump/llvm-cxxdump.cpp index a21acae02c3a..447d55afa8b8 100644 --- a/tools/llvm-vtabledump/llvm-vtabledump.cpp +++ b/tools/llvm-cxxdump/llvm-cxxdump.cpp @@ -1,4 +1,4 @@ -//===- llvm-vtabledump.cpp - Dump vtables in an Object File -----*- C++ -*-===// +//===- llvm-cxxdump.cpp - Dump C++ data in an Object File -------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -7,12 +7,11 @@ // //===----------------------------------------------------------------------===// // -// Dumps VTables resident in object files and archives. Note, it currently only -// supports MS-ABI style object files. +// Dumps C++ data resident in object files and archives. // //===----------------------------------------------------------------------===// -#include "llvm-vtabledump.h" +#include "llvm-cxxdump.h" #include "Error.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Object/Archive.h" @@ -25,6 +24,7 @@ #include "llvm/Support/Signals.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" +#include "llvm/Support/raw_ostream.h" #include <map> #include <string> #include <system_error> @@ -43,7 +43,7 @@ static int ReturnValue = EXIT_SUCCESS; namespace llvm { -bool error(std::error_code EC) { +static bool error(std::error_code EC) { if (!EC) return false; @@ -135,7 +135,7 @@ static bool collectRelocationOffsets( return false; } -static void dumpVTables(const ObjectFile *Obj) { +static void dumpCXXData(const ObjectFile *Obj) { struct CompleteObjectLocator { StringRef Symbols[2]; ArrayRef<little32_t> Data; @@ -153,13 +153,32 @@ static void dumpVTables(const ObjectFile *Obj) { uint64_t AlwaysZero; StringRef MangledName; }; + struct ThrowInfo { + uint32_t Flags; + }; + struct CatchableTypeArray { + uint32_t NumEntries; + }; + struct CatchableType { + uint32_t Flags; + uint32_t NonVirtualBaseAdjustmentOffset; + int32_t VirtualBasePointerOffset; + uint32_t VirtualBaseAdjustmentOffset; + uint32_t Size; + StringRef Symbols[2]; + }; std::map<std::pair<StringRef, uint64_t>, StringRef> VFTableEntries; + std::map<std::pair<StringRef, uint64_t>, StringRef> TIEntries; + std::map<std::pair<StringRef, uint64_t>, StringRef> CTAEntries; std::map<StringRef, ArrayRef<little32_t>> VBTables; std::map<StringRef, CompleteObjectLocator> COLs; std::map<StringRef, ClassHierarchyDescriptor> CHDs; std::map<std::pair<StringRef, uint64_t>, StringRef> BCAEntries; std::map<StringRef, BaseClassDescriptor> BCDs; std::map<StringRef, TypeDescriptor> TDs; + std::map<StringRef, ThrowInfo> TIs; + std::map<StringRef, CatchableTypeArray> CTAs; + std::map<StringRef, CatchableType> CTs; std::map<std::pair<StringRef, uint64_t>, StringRef> VTableSymEntries; std::map<std::pair<StringRef, uint64_t>, int64_t> VTableDataEntries; @@ -265,6 +284,39 @@ static void dumpVTables(const ObjectFile *Obj) { return; TDs[SymName] = TD; } + // Throw descriptors in the MS-ABI start with '_TI' + else if (SymName.startswith("_TI") || SymName.startswith("__TI")) { + ThrowInfo TI; + TI.Flags = *reinterpret_cast<const little32_t *>(SymContents.data()); + collectRelocationOffsets(Obj, Sec, SecAddress, SymAddress, SymSize, + SymName, TIEntries); + TIs[SymName] = TI; + } + // Catchable type arrays in the MS-ABI start with _CTA or __CTA. + else if (SymName.startswith("_CTA") || SymName.startswith("__CTA")) { + CatchableTypeArray CTA; + CTA.NumEntries = + *reinterpret_cast<const little32_t *>(SymContents.data()); + collectRelocationOffsets(Obj, Sec, SecAddress, SymAddress, SymSize, + SymName, CTAEntries); + CTAs[SymName] = CTA; + } + // Catchable types in the MS-ABI start with _CT or __CT. + else if (SymName.startswith("_CT") || SymName.startswith("__CT")) { + const little32_t *DataPtr = + reinterpret_cast<const little32_t *>(SymContents.data()); + CatchableType CT; + CT.Flags = DataPtr[0]; + CT.NonVirtualBaseAdjustmentOffset = DataPtr[2]; + CT.VirtualBasePointerOffset = DataPtr[3]; + 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; + CTs[SymName] = CT; + } // Construction vtables in the Itanium ABI start with '_ZTT' or '__ZTT'. else if (SymName.startswith("_ZTT") || SymName.startswith("__ZTT")) { collectRelocationOffsets(Obj, Sec, SecAddress, SymAddress, SymSize, @@ -282,7 +334,8 @@ static void dumpVTables(const ObjectFile *Obj) { auto Key = std::make_pair(SymName, SymOffI); if (VTableSymEntries.count(Key)) continue; - const char *DataPtr = SymContents.substr(SymOffI, BytesInAddress).data(); + const char *DataPtr = + SymContents.substr(SymOffI, BytesInAddress).data(); int64_t VData; if (BytesInAddress == 8) VData = *reinterpret_cast<const little64_t *>(DataPtr); @@ -296,14 +349,13 @@ static void dumpVTables(const ObjectFile *Obj) { // FIXME: Do something with these! } } - for (const std::pair<std::pair<StringRef, uint64_t>, StringRef> &VFTableEntry : - VFTableEntries) { + for (const auto &VFTableEntry : VFTableEntries) { StringRef VFTableName = VFTableEntry.first.first; uint64_t Offset = VFTableEntry.first.second; StringRef SymName = VFTableEntry.second; outs() << VFTableName << '[' << Offset << "]: " << SymName << '\n'; } - for (const std::pair<StringRef, ArrayRef<little32_t>> &VBTable : VBTables) { + for (const auto &VBTable : VBTables) { StringRef VBTableName = VBTable.first; uint32_t Idx = 0; for (little32_t Offset : VBTable.second) { @@ -311,16 +363,17 @@ static void dumpVTables(const ObjectFile *Obj) { Idx += sizeof(Offset); } } - for (const std::pair<StringRef, CompleteObjectLocator> &COLPair : COLs) { + for (const auto &COLPair : COLs) { StringRef COLName = COLPair.first; const CompleteObjectLocator &COL = COLPair.second; outs() << COLName << "[IsImageRelative]: " << COL.Data[0] << '\n'; outs() << COLName << "[OffsetToTop]: " << COL.Data[1] << '\n'; outs() << COLName << "[VFPtrOffset]: " << COL.Data[2] << '\n'; outs() << COLName << "[TypeDescriptor]: " << COL.Symbols[0] << '\n'; - outs() << COLName << "[ClassHierarchyDescriptor]: " << COL.Symbols[1] << '\n'; + outs() << COLName << "[ClassHierarchyDescriptor]: " << COL.Symbols[1] + << '\n'; } - for (const std::pair<StringRef, ClassHierarchyDescriptor> &CHDPair : CHDs) { + for (const auto &CHDPair : CHDs) { StringRef CHDName = CHDPair.first; const ClassHierarchyDescriptor &CHD = CHDPair.second; outs() << CHDName << "[AlwaysZero]: " << CHD.Data[0] << '\n'; @@ -328,14 +381,13 @@ static void dumpVTables(const ObjectFile *Obj) { outs() << CHDName << "[NumClasses]: " << CHD.Data[2] << '\n'; outs() << CHDName << "[BaseClassArray]: " << CHD.Symbols[0] << '\n'; } - for (const std::pair<std::pair<StringRef, uint64_t>, StringRef> &BCAEntry : - BCAEntries) { + for (const auto &BCAEntry : BCAEntries) { StringRef BCAName = BCAEntry.first.first; uint64_t Offset = BCAEntry.first.second; StringRef SymName = BCAEntry.second; outs() << BCAName << '[' << Offset << "]: " << SymName << '\n'; } - for (const std::pair<StringRef, BaseClassDescriptor> &BCDPair : BCDs) { + for (const auto &BCDPair : BCDs) { StringRef BCDName = BCDPair.first; const BaseClassDescriptor &BCD = BCDPair.second; outs() << BCDName << "[TypeDescriptor]: " << BCD.Symbols[0] << '\n'; @@ -344,9 +396,10 @@ static void dumpVTables(const ObjectFile *Obj) { outs() << BCDName << "[VBPtrOffset]: " << BCD.Data[2] << '\n'; outs() << BCDName << "[OffsetInVBTable]: " << BCD.Data[3] << '\n'; outs() << BCDName << "[Flags]: " << BCD.Data[4] << '\n'; - outs() << BCDName << "[ClassHierarchyDescriptor]: " << BCD.Symbols[1] << '\n'; + outs() << BCDName << "[ClassHierarchyDescriptor]: " << BCD.Symbols[1] + << '\n'; } - for (const std::pair<StringRef, TypeDescriptor> &TDPair : TDs) { + for (const auto &TDPair : TDs) { StringRef TDName = TDPair.first; const TypeDescriptor &TD = TDPair.second; outs() << TDName << "[VFPtr]: " << TD.Symbols[0] << '\n'; @@ -356,14 +409,67 @@ static void dumpVTables(const ObjectFile *Obj) { /*UseHexEscapes=*/true) << '\n'; } - for (const std::pair<std::pair<StringRef, uint64_t>, StringRef> &VTTPair : - VTTEntries) { + for (const auto &TIPair : TIs) { + StringRef TIName = TIPair.first; + const ThrowInfo &TI = TIPair.second; + auto dumpThrowInfoFlag = [&](const char *Name, uint32_t Flag) { + outs() << TIName << "[Flags." << Name + << "]: " << (TI.Flags & Flag ? "true" : "false") << '\n'; + }; + auto dumpThrowInfoSymbol = [&](const char *Name, int Offset) { + outs() << TIName << '[' << Name << "]: "; + auto Entry = TIEntries.find(std::make_pair(TIName, Offset)); + outs() << (Entry == TIEntries.end() ? "null" : Entry->second) << '\n'; + }; + outs() << TIName << "[Flags]: " << TI.Flags << '\n'; + dumpThrowInfoFlag("Const", 1); + dumpThrowInfoFlag("Volatile", 2); + dumpThrowInfoSymbol("CleanupFn", 4); + dumpThrowInfoSymbol("ForwardCompat", 8); + dumpThrowInfoSymbol("CatchableTypeArray", 12); + } + for (const auto &CTAPair : CTAs) { + StringRef CTAName = CTAPair.first; + const CatchableTypeArray &CTA = CTAPair.second; + + outs() << CTAName << "[NumEntries]: " << CTA.NumEntries << '\n'; + + unsigned Idx = 0; + for (auto I = CTAEntries.lower_bound(std::make_pair(CTAName, 0)), + E = CTAEntries.upper_bound(std::make_pair(CTAName, UINT64_MAX)); + I != E; ++I) + outs() << CTAName << '[' << Idx++ << "]: " << I->second << '\n'; + } + for (const auto &CTPair : CTs) { + StringRef CTName = CTPair.first; + const CatchableType &CT = CTPair.second; + auto dumpCatchableTypeFlag = [&](const char *Name, uint32_t Flag) { + outs() << CTName << "[Flags." << Name + << "]: " << (CT.Flags & Flag ? "true" : "false") << '\n'; + }; + outs() << CTName << "[Flags]: " << CT.Flags << '\n'; + dumpCatchableTypeFlag("ScalarType", 1); + dumpCatchableTypeFlag("VirtualInheritance", 4); + outs() << CTName << "[TypeDescriptor]: " << CT.Symbols[0] << '\n'; + outs() << CTName << "[NonVirtualBaseAdjustmentOffset]: " + << CT.NonVirtualBaseAdjustmentOffset << '\n'; + outs() << CTName + << "[VirtualBasePointerOffset]: " << CT.VirtualBasePointerOffset + << '\n'; + outs() << CTName << "[VirtualBaseAdjustmentOffset]: " + << CT.VirtualBaseAdjustmentOffset << '\n'; + outs() << CTName << "[Size]: " << CT.Size << '\n'; + outs() << CTName + << "[CopyCtor]: " << (CT.Symbols[1].empty() ? "null" : CT.Symbols[1]) + << '\n'; + } + for (const auto &VTTPair : VTTEntries) { StringRef VTTName = VTTPair.first.first; uint64_t VTTOffset = VTTPair.first.second; StringRef VTTEntry = VTTPair.second; outs() << VTTName << '[' << VTTOffset << "]: " << VTTEntry << '\n'; } - for (const std::pair<StringRef, StringRef> &TIPair : TINames) { + for (const auto &TIPair : TINames) { StringRef TIName = TIPair.first; outs() << TIName << ": " << TIPair.second << '\n'; } @@ -410,17 +516,16 @@ static void dumpArchive(const Archive *Arc) { } if (ObjectFile *Obj = dyn_cast<ObjectFile>(&*ChildOrErr.get())) - dumpVTables(Obj); + dumpCXXData(Obj); else - reportError(Arc->getFileName(), - vtabledump_error::unrecognized_file_format); + reportError(Arc->getFileName(), cxxdump_error::unrecognized_file_format); } } static void dumpInput(StringRef File) { // If file isn't stdin, check that it exists. if (File != "-" && !sys::fs::exists(File)) { - reportError(File, vtabledump_error::file_not_found); + reportError(File, cxxdump_error::file_not_found); return; } @@ -435,9 +540,9 @@ static void dumpInput(StringRef File) { if (Archive *Arc = dyn_cast<Archive>(&Binary)) dumpArchive(Arc); else if (ObjectFile *Obj = dyn_cast<ObjectFile>(&Binary)) - dumpVTables(Obj); + dumpCXXData(Obj); else - reportError(File, vtabledump_error::unrecognized_file_format); + reportError(File, cxxdump_error::unrecognized_file_format); } int main(int argc, const char *argv[]) { @@ -451,7 +556,7 @@ int main(int argc, const char *argv[]) { // Register the target printer for --version. cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion); - cl::ParseCommandLineOptions(argc, argv, "LLVM VTable Dumper\n"); + cl::ParseCommandLineOptions(argc, argv, "LLVM C++ ABI Data Dumper\n"); // Default to stdin if no filename is specified. if (opts::InputFilenames.size() == 0) diff --git a/tools/llvm-vtabledump/llvm-vtabledump.h b/tools/llvm-cxxdump/llvm-cxxdump.h index 62f755739c39..daa05cb2ca0a 100644 --- a/tools/llvm-vtabledump/llvm-vtabledump.h +++ b/tools/llvm-cxxdump/llvm-cxxdump.h @@ -1,4 +1,4 @@ -//===-- llvm-vtabledump.h ---------------------------------------*- C++ -*-===// +//===-- llvm-cxxdump.h ------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -7,8 +7,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_TOOLS_LLVM_VTABLEDUMP_LLVM_VTABLEDUMP_H -#define LLVM_TOOLS_LLVM_VTABLEDUMP_LLVM_VTABLEDUMP_H +#ifndef LLVM_TOOLS_LLVM_CXXDUMP_LLVM_CXXDUMP_H +#define LLVM_TOOLS_LLVM_CXXDUMP_LLVM_CXXDUMP_H #include "llvm/Support/CommandLine.h" #include <string> @@ -17,7 +17,7 @@ namespace opts { extern llvm::cl::list<std::string> InputFilenames; } // namespace opts -#define LLVM_VTABLEDUMP_ENUM_ENT(ns, enum) \ +#define LLVM_CXXDUMP_ENUM_ENT(ns, enum) \ { #enum, ns::enum } #endif diff --git a/tools/llvm-dis/llvm-dis.cpp b/tools/llvm-dis/llvm-dis.cpp index 1349ecc85a4c..26f14b9b1a28 100644 --- a/tools/llvm-dis/llvm-dis.cpp +++ b/tools/llvm-dis/llvm-dis.cpp @@ -54,16 +54,18 @@ static cl::opt<bool> ShowAnnotations("show-annotations", cl::desc("Add informational comments to the .ll file")); +static cl::opt<bool> PreserveAssemblyUseListOrder( + "preserve-ll-uselistorder", + cl::desc("Preserve use-list order when writing LLVM assembly."), + cl::init(false), cl::Hidden); + namespace { static void printDebugLoc(const DebugLoc &DL, formatted_raw_ostream &OS) { OS << DL.getLine() << ":" << DL.getCol(); - if (MDNode *N = DL.getInlinedAt(getGlobalContext())) { - DebugLoc IDL = DebugLoc::getFromDILocation(N); - if (!IDL.isUnknown()) { - OS << "@"; - printDebugLoc(IDL,OS); - } + if (DILocation *IDL = DL.getInlinedAt()) { + OS << "@"; + printDebugLoc(IDL, OS); } } class CommentWriter : public AssemblyAnnotationWriter { @@ -78,11 +80,11 @@ public: if (!V.getType()->isVoidTy()) { OS.PadToColumn(50); Padded = true; - OS << "; [#uses=" << V.getNumUses() << " type=" << *V.getType() << "]"; // Output # uses and type + // Output # uses and type + OS << "; [#uses=" << V.getNumUses() << " type=" << *V.getType() << "]"; } if (const Instruction *I = dyn_cast<Instruction>(&V)) { - const DebugLoc &DL = I->getDebugLoc(); - if (!DL.isUnknown()) { + if (const DebugLoc &DL = I->getDebugLoc()) { if (!Padded) { OS.PadToColumn(50); Padded = true; @@ -93,20 +95,18 @@ public: OS << "]"; } if (const DbgDeclareInst *DDI = dyn_cast<DbgDeclareInst>(I)) { - DIVariable Var(DDI->getVariable()); if (!Padded) { OS.PadToColumn(50); OS << ";"; } - OS << " [debug variable = " << Var.getName() << "]"; + OS << " [debug variable = " << DDI->getVariable()->getName() << "]"; } else if (const DbgValueInst *DVI = dyn_cast<DbgValueInst>(I)) { - DIVariable Var(DVI->getVariable()); if (!Padded) { OS.PadToColumn(50); OS << ";"; } - OS << " [debug variable = " << Var.getName() << "]"; + OS << " [debug variable = " << DVI->getVariable()->getName() << "]"; } } } @@ -115,14 +115,21 @@ public: } // end anon namespace static void diagnosticHandler(const DiagnosticInfo &DI, void *Context) { - assert(DI.getSeverity() == DS_Error && "Only expecting errors"); - raw_ostream &OS = errs(); OS << (char *)Context << ": "; + 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; + } + DiagnosticPrinterRawOStream DP(OS); DI.print(DP); OS << '\n'; - exit(1); + + if (DI.getSeverity() == DS_Error) + exit(1); } int main(int argc, char **argv) { @@ -152,6 +159,9 @@ int main(int argc, char **argv) { getStreamedBitcodeModule(DisplayFilename, Streamer, Context); M = std::move(*MOrErr); M->materializeAllPermanently(); + } else { + errs() << argv[0] << ": " << ErrorMessage << '\n'; + return 1; } // Just use stdout. We won't actually print anything on it. @@ -162,13 +172,9 @@ int main(int argc, char **argv) { if (InputFilename == "-") { OutputFilename = "-"; } else { - const std::string &IFN = InputFilename; - int Len = IFN.length(); - // If the source ends in .bc, strip it off. - if (IFN[Len-3] == '.' && IFN[Len-2] == 'b' && IFN[Len-1] == 'c') - OutputFilename = std::string(IFN.begin(), IFN.end()-3)+".ll"; - else - OutputFilename = IFN+".ll"; + StringRef IFN = InputFilename; + OutputFilename = (IFN.endswith(".bc") ? IFN.drop_back(3) : IFN).str(); + OutputFilename += ".ll"; } } @@ -186,7 +192,7 @@ int main(int argc, char **argv) { // All that llvm-dis does is write the assembly to a file. if (!DontPrint) - M->print(Out->os(), Annotator.get()); + M->print(Out->os(), Annotator.get(), PreserveAssemblyUseListOrder); // Declare success. Out->keep(); diff --git a/tools/llvm-dwarfdump/CMakeLists.txt b/tools/llvm-dwarfdump/CMakeLists.txt index 288b3237010b..086b13974614 100644 --- a/tools/llvm-dwarfdump/CMakeLists.txt +++ b/tools/llvm-dwarfdump/CMakeLists.txt @@ -1,5 +1,5 @@ set(LLVM_LINK_COMPONENTS - DebugInfo + DebugInfoDWARF Object Support ) diff --git a/tools/llvm-dwarfdump/LLVMBuild.txt b/tools/llvm-dwarfdump/LLVMBuild.txt index 28b7c4cda480..a9dca3e0cfff 100644 --- a/tools/llvm-dwarfdump/LLVMBuild.txt +++ b/tools/llvm-dwarfdump/LLVMBuild.txt @@ -19,4 +19,4 @@ type = Tool name = llvm-dwarfdump parent = Tools -required_libraries = DebugInfo Object +required_libraries = DebugInfoDWARF Object diff --git a/tools/llvm-dwarfdump/Makefile b/tools/llvm-dwarfdump/Makefile index 7ca1a8d877d0..00b18c60bb2a 100644 --- a/tools/llvm-dwarfdump/Makefile +++ b/tools/llvm-dwarfdump/Makefile @@ -9,7 +9,7 @@ LEVEL := ../.. TOOLNAME := llvm-dwarfdump -LINK_COMPONENTS := DebugInfo Object +LINK_COMPONENTS := DebugInfoDWARF Object # This tool has no plugins, optimize startup time. TOOL_NO_EXPORTS := 1 diff --git a/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/tools/llvm-dwarfdump/llvm-dwarfdump.cpp index cff8216403b7..c1cb0218ac38 100644 --- a/tools/llvm-dwarfdump/llvm-dwarfdump.cpp +++ b/tools/llvm-dwarfdump/llvm-dwarfdump.cpp @@ -14,6 +14,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Triple.h" #include "llvm/DebugInfo/DIContext.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Object/RelocVisitor.h" #include "llvm/Support/CommandLine.h" @@ -86,7 +87,7 @@ static void DumpInput(StringRef Filename) { } ObjectFile &Obj = *ObjOrErr.get(); - std::unique_ptr<DIContext> DICtx(DIContext::getDWARFContext(Obj)); + std::unique_ptr<DIContext> DICtx(new DWARFContextInMemory(Obj)); outs() << Filename << ":\tfile format " << Obj.getFileFormatName() << "\n\n"; diff --git a/tools/llvm-extract/llvm-extract.cpp b/tools/llvm-extract/llvm-extract.cpp index 53b2f0d00f41..936496cd7fe6 100644 --- a/tools/llvm-extract/llvm-extract.cpp +++ b/tools/llvm-extract/llvm-extract.cpp @@ -20,7 +20,7 @@ #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IRReader/IRReader.h" -#include "llvm/PassManager.h" +#include "llvm/IR/LegacyPassManager.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/ManagedStatic.h" @@ -90,6 +90,16 @@ static cl::opt<bool> OutputAssembly("S", cl::desc("Write output as LLVM assembly"), cl::Hidden); +static cl::opt<bool> PreserveBitcodeUseListOrder( + "preserve-bc-uselistorder", + cl::desc("Preserve use-list order when writing LLVM bitcode."), + cl::init(true), cl::Hidden); + +static cl::opt<bool> PreserveAssemblyUseListOrder( + "preserve-ll-uselistorder", + cl::desc("Preserve use-list order when writing LLVM assembly."), + cl::init(false), cl::Hidden); + int main(int argc, char **argv) { // Print a stack trace if we signal out. sys::PrintStackTraceOnErrorSignal(); @@ -246,8 +256,7 @@ int main(int argc, char **argv) { // In addition to deleting all other functions, we also want to spiff it // up a little bit. Do this now. - PassManager Passes; - Passes.add(new DataLayoutPass()); // Use correct DataLayout + legacy::PassManager Passes; std::vector<GlobalValue*> Gvs(GVs.begin(), GVs.end()); @@ -265,9 +274,10 @@ int main(int argc, char **argv) { } if (OutputAssembly) - Passes.add(createPrintModulePass(Out.os())); + Passes.add( + createPrintModulePass(Out.os(), "", PreserveAssemblyUseListOrder)); else if (Force || !CheckBitcodeOutputToConsole(Out.os(), true)) - Passes.add(createBitcodeWriterPass(Out.os())); + Passes.add(createBitcodeWriterPass(Out.os(), PreserveBitcodeUseListOrder)); Passes.run(*M.get()); diff --git a/tools/llvm-go/llvm-go.go b/tools/llvm-go/llvm-go.go index cf520bd90e9d..c5c3fd244cad 100644 --- a/tools/llvm-go/llvm-go.go +++ b/tools/llvm-go/llvm-go.go @@ -46,7 +46,7 @@ var components = []string{ "bitwriter", "codegen", "core", - "debuginfo", + "debuginfodwarf", "executionengine", "instrumentation", "interpreter", @@ -137,7 +137,7 @@ type (run_build_sh int) `, flags.cpp, flags.cxx, flags.ld) } -func runGoWithLLVMEnv(args []string, cc, cxx, llgo, cppflags, cxxflags, ldflags string) { +func runGoWithLLVMEnv(args []string, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags string) { args = addTag(args, "byollvm") srcdir := llvmConfig("--src-root") @@ -162,26 +162,6 @@ func runGoWithLLVMEnv(args []string, cc, cxx, llgo, cppflags, cxxflags, ldflags newpath := os.Getenv("PATH") - if llgo != "" { - bindir := filepath.Join(tmpgopath, "bin") - - err = os.MkdirAll(bindir, os.ModePerm) - if err != nil { - panic(err.Error()) - } - - err = os.Symlink(llgo, filepath.Join(bindir, "gccgo")) - if err != nil { - panic(err.Error()) - } - - newpathlist := []string{bindir} - newpathlist = append(newpathlist, filepath.SplitList(newpath)...) - newpath = strings.Join(newpathlist, string(filepath.ListSeparator)) - - args = append([]string{args[0], "-compiler", "gccgo"}, args[1:]...) - } - newgopathlist := []string{tmpgopath} newgopathlist = append(newgopathlist, filepath.SplitList(os.Getenv("GOPATH"))...) newgopath := strings.Join(newgopathlist, string(filepath.ListSeparator)) @@ -197,24 +177,29 @@ func runGoWithLLVMEnv(args []string, cc, cxx, llgo, cppflags, cxxflags, ldflags "GOPATH=" + newgopath, "PATH=" + newpath, } + if llgo != "" { + newenv = append(newenv, "GCCGO=" + llgo) + } + for _, v := range os.Environ() { if !strings.HasPrefix(v, "CC=") && !strings.HasPrefix(v, "CXX=") && !strings.HasPrefix(v, "CGO_CPPFLAGS=") && !strings.HasPrefix(v, "CGO_CXXFLAGS=") && !strings.HasPrefix(v, "CGO_LDFLAGS=") && + !strings.HasPrefix(v, "GCCGO=") && !strings.HasPrefix(v, "GOPATH=") && !strings.HasPrefix(v, "PATH=") { newenv = append(newenv, v) } } - gocmdpath, err := exec.LookPath("go") + gocmdpath, err := exec.LookPath(gocmd) if err != nil { panic(err.Error()) } - proc, err := os.StartProcess(gocmdpath, append([]string{"go"}, args...), + proc, err := os.StartProcess(gocmdpath, append([]string{gocmd}, args...), &os.ProcAttr{ Env: newenv, Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}, @@ -247,6 +232,7 @@ func main() { cppflags := os.Getenv("CGO_CPPFLAGS") cxxflags := os.Getenv("CGO_CXXFLAGS") ldflags := os.Getenv("CGO_LDFLAGS") + gocmd := "go" llgo := "" args := os.Args[1:] @@ -260,6 +246,9 @@ func main() { 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:] @@ -279,7 +268,7 @@ func main() { switch args[0] { case "build", "get", "install", "run", "test": - runGoWithLLVMEnv(args, cc, cxx, llgo, cppflags, cxxflags, ldflags) + runGoWithLLVMEnv(args, cc, cxx, gocmd, llgo, cppflags, cxxflags, ldflags) case "print-components": printComponents() case "print-config": diff --git a/tools/llvm-jitlistener/Makefile b/tools/llvm-jitlistener/Makefile index 6d72427e96e8..4acb6a561715 100644 --- a/tools/llvm-jitlistener/Makefile +++ b/tools/llvm-jitlistener/Makefile @@ -1,27 +1,27 @@ -##===- tools/llvm-jitlistener/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-jitlistener
-
-include $(LEVEL)/Makefile.config
-
-LINK_COMPONENTS := mcjit interpreter nativecodegen bitreader asmparser irreader selectiondag Object
-
-# If Intel JIT Events support is configured, link against the LLVM Intel JIT
-# Events interface library. If not, this tool will do nothing useful, but it
-# will build correctly.
-ifeq ($(USE_INTEL_JITEVENTS), 1)
- LINK_COMPONENTS += debuginfo inteljitevents
-endif
-
-# This tool has no plugins, optimize startup time.
-TOOL_NO_EXPORTS := 1
-
-include $(LLVM_SRC_ROOT)/Makefile.rules
+##===- tools/llvm-jitlistener/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-jitlistener + +include $(LEVEL)/Makefile.config + +LINK_COMPONENTS := mcjit interpreter nativecodegen bitreader asmparser irreader selectiondag Object + +# If Intel JIT Events support is configured, link against the LLVM Intel JIT +# Events interface library. If not, this tool will do nothing useful, but it +# will build correctly. +ifeq ($(USE_INTEL_JITEVENTS), 1) + LINK_COMPONENTS += debuginfodwarf inteljitevents +endif + +# This tool has no plugins, optimize startup time. +TOOL_NO_EXPORTS := 1 + +include $(LLVM_SRC_ROOT)/Makefile.rules diff --git a/tools/llvm-jitlistener/llvm-jitlistener.cpp b/tools/llvm-jitlistener/llvm-jitlistener.cpp index c3091a559550..af1a59bdbd37 100644 --- a/tools/llvm-jitlistener/llvm-jitlistener.cpp +++ b/tools/llvm-jitlistener/llvm-jitlistener.cpp @@ -13,12 +13,12 @@ // //===----------------------------------------------------------------------===// -#include "llvm/IR/LLVMContext.h" #include "../../lib/ExecutionEngine/IntelJITEvents/IntelJITEventsWrapper.h" #include "llvm/ADT/Triple.h" #include "llvm/ExecutionEngine/JITEventListener.h" #include "llvm/ExecutionEngine/MCJIT.h" #include "llvm/ExecutionEngine/SectionMemoryManager.h" +#include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IRReader/IRReader.h" #include "llvm/Support/CommandLine.h" @@ -30,6 +30,7 @@ #include "llvm/Support/Signals.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/TargetSelect.h" +#include "llvm/Support/raw_ostream.h" #include <string> using namespace llvm; diff --git a/tools/llvm-link/llvm-link.cpp b/tools/llvm-link/llvm-link.cpp index 828b9bb8ef70..369f3477fe5c 100644 --- a/tools/llvm-link/llvm-link.cpp +++ b/tools/llvm-link/llvm-link.cpp @@ -13,7 +13,9 @@ //===----------------------------------------------------------------------===// #include "llvm/Linker/Linker.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/IR/AutoUpgrade.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/LLVMContext.h" @@ -36,6 +38,11 @@ static cl::list<std::string> InputFilenames(cl::Positional, cl::OneOrMore, cl::desc("<input bitcode files>")); +static cl::list<std::string> OverridingInputs( + "override", cl::ZeroOrMore, cl::value_desc("filename"), + cl::desc( + "input bitcode file which can override previously defined symbol(s)")); + static cl::opt<std::string> OutputFilename("o", cl::desc("Override output filename"), cl::init("-"), cl::value_desc("filename")); @@ -57,6 +64,16 @@ static cl::opt<bool> SuppressWarnings("suppress-warnings", cl::desc("Suppress all linking warnings"), cl::init(false)); +static cl::opt<bool> PreserveBitcodeUseListOrder( + "preserve-bc-uselistorder", + cl::desc("Preserve use-list order when writing LLVM bitcode."), + cl::init(true), cl::Hidden); + +static cl::opt<bool> PreserveAssemblyUseListOrder( + "preserve-ll-uselistorder", + cl::desc("Preserve use-list order when writing LLVM assembly."), + cl::init(false), cl::Hidden); + // Read the specified bitcode file in and return it. This routine searches the // link path for the specified file to try to find it... // @@ -68,6 +85,9 @@ loadFile(const char *argv0, const std::string &FN, LLVMContext &Context) { if (!Result) Err.print(argv0, errs()); + Result->materializeMetadata(); + UpgradeDebugInfo(*Result); + return Result; } @@ -92,6 +112,31 @@ static void diagnosticHandler(const DiagnosticInfo &DI) { errs() << '\n'; } +static bool linkFiles(const char *argv0, LLVMContext &Context, Linker &L, + const cl::list<std::string> &Files, + bool OverrideDuplicateSymbols) { + for (const auto &File : Files) { + std::unique_ptr<Module> M = loadFile(argv0, File, Context); + if (!M.get()) { + errs() << argv0 << ": error loading file '" << File << "'\n"; + return false; + } + + if (verifyModule(*M, &errs())) { + errs() << argv0 << ": " << File << ": error: input module is broken!\n"; + return false; + } + + if (Verbose) + errs() << "Linking in '" << File << "'\n"; + + if (L.linkInModule(M.get(), OverrideDuplicateSymbols)) + return false; + } + + return true; +} + int main(int argc, char **argv) { // Print a stack trace if we signal out. sys::PrintStackTraceOnErrorSignal(); @@ -104,18 +149,13 @@ int main(int argc, char **argv) { auto Composite = make_unique<Module>("llvm-link", Context); Linker L(Composite.get(), diagnosticHandler); - for (unsigned i = 0; i < InputFilenames.size(); ++i) { - std::unique_ptr<Module> M = loadFile(argv[0], InputFilenames[i], Context); - if (!M.get()) { - errs() << argv[0] << ": error loading file '" <<InputFilenames[i]<< "'\n"; - return 1; - } - - if (Verbose) errs() << "Linking in '" << InputFilenames[i] << "'\n"; + // First add all the regular input files + if (!linkFiles(argv[0], Context, L, InputFilenames, false)) + return 1; - if (L.linkInModule(M.get())) - return 1; - } + // Next the -override ones. + if (!linkFiles(argv[0], Context, L, OverridingInputs, true)) + return 1; if (DumpAsm) errs() << "Here's the assembly:\n" << *Composite; @@ -126,16 +166,16 @@ int main(int argc, char **argv) { return 1; } - if (verifyModule(*Composite)) { - errs() << argv[0] << ": linked module is broken!\n"; + if (verifyModule(*Composite, &errs())) { + errs() << argv[0] << ": error: linked module is broken!\n"; return 1; } if (Verbose) errs() << "Writing bitcode...\n"; if (OutputAssembly) { - Out.os() << *Composite; + Composite->print(Out.os(), nullptr, PreserveAssemblyUseListOrder); } else if (Force || !CheckBitcodeOutputToConsole(Out.os(), true)) - WriteBitcodeToFile(Composite.get(), Out.os()); + WriteBitcodeToFile(Composite.get(), Out.os(), PreserveBitcodeUseListOrder); // Declare success. Out.keep(); diff --git a/tools/llvm-lto/llvm-lto.cpp b/tools/llvm-lto/llvm-lto.cpp index 9cd031eaf8d3..9cd6587c2622 100644 --- a/tools/llvm-lto/llvm-lto.cpp +++ b/tools/llvm-lto/llvm-lto.cpp @@ -26,9 +26,13 @@ using namespace llvm; -static cl::opt<bool> -DisableOpt("disable-opt", cl::init(false), - cl::desc("Do not run any optimization passes")); +static cl::opt<char> +OptLevel("O", + cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] " + "(default = '-O2')"), + cl::Prefix, + cl::ZeroOrMore, + cl::init('2')); static cl::opt<bool> DisableInline("disable-inlining", cl::init(false), @@ -69,14 +73,18 @@ static cl::opt<bool> ListSymbolsOnly( "list-symbols-only", cl::init(false), cl::desc("Instead of running LTO, list the symbols in each IR file")); +static cl::opt<bool> SetMergedModule( + "set-merged-module", cl::init(false), + cl::desc("Use the first input module as the merged module")); + namespace { struct ModuleInfo { std::vector<bool> CanBeHidden; }; } -void handleDiagnostics(lto_codegen_diagnostic_severity_t Severity, - const char *Msg, void *) { +static void handleDiagnostics(lto_codegen_diagnostic_severity_t Severity, + const char *Msg, void *) { switch (Severity) { case LTO_DS_NOTE: errs() << "note: "; @@ -94,7 +102,7 @@ void handleDiagnostics(lto_codegen_diagnostic_severity_t Severity, errs() << Msg << "\n"; } -std::unique_ptr<LTOModule> +static std::unique_ptr<LTOModule> getLocalLTOModule(StringRef Path, std::unique_ptr<MemoryBuffer> &Buffer, const TargetOptions &Options, std::string &Error) { ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = @@ -114,7 +122,7 @@ 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. -int listSymbols(StringRef Command, const TargetOptions &Options) { +static int listSymbols(StringRef Command, const TargetOptions &Options) { for (auto &Filename : InputFilenames) { std::string Error; std::unique_ptr<MemoryBuffer> Buffer; @@ -142,6 +150,11 @@ 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; + } + // Initialize the configured targets. InitializeAllTargets(); InitializeAllTargetMCs(); @@ -194,15 +207,22 @@ int main(int argc, char **argv) { return 1; } - if (!CodeGen.addModule(Module.get())) + LTOModule *LTOMod = Module.get(); + + // 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()); + } else if (!CodeGen.addModule(Module.get())) return 1; - unsigned NumSyms = Module->getSymbolCount(); + unsigned NumSyms = LTOMod->getSymbolCount(); for (unsigned I = 0; I < NumSyms; ++I) { - StringRef Name = Module->getSymbolName(I); + StringRef Name = LTOMod->getSymbolName(I); if (!DSOSymbolsSet.count(Name)) continue; - lto_symbol_attributes Attrs = Module->getSymbolAttributes(I); + 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); @@ -217,6 +237,11 @@ int main(int argc, char **argv) { for (unsigned i = 0; i < KeptDSOSyms.size(); ++i) CodeGen.addMustPreserveSymbol(KeptDSOSyms[i].c_str()); + // Set cpu and attrs strings for the default target/subtarget. + CodeGen.setCpu(MCPU.c_str()); + + CodeGen.setOptLevel(OptLevel - '0'); + std::string attrs; for (unsigned i = 0; i < MAttrs.size(); ++i) { if (i > 0) @@ -231,7 +256,7 @@ int main(int argc, char **argv) { size_t len = 0; std::string ErrorInfo; const void *Code = - CodeGen.compile(&len, DisableOpt, DisableInline, DisableGVNLoadPRE, + CodeGen.compile(&len, DisableInline, DisableGVNLoadPRE, DisableLTOVectorization, ErrorInfo); if (!Code) { errs() << argv[0] @@ -251,7 +276,7 @@ int main(int argc, char **argv) { } else { std::string ErrorInfo; const char *OutputName = nullptr; - if (!CodeGen.compile_to_file(&OutputName, DisableOpt, DisableInline, + if (!CodeGen.compile_to_file(&OutputName, DisableInline, DisableGVNLoadPRE, DisableLTOVectorization, ErrorInfo)) { errs() << argv[0] diff --git a/tools/llvm-mc/CMakeLists.txt b/tools/llvm-mc/CMakeLists.txt index 6f8e9e5405f9..15c6dda1b258 100644 --- a/tools/llvm-mc/CMakeLists.txt +++ b/tools/llvm-mc/CMakeLists.txt @@ -1,5 +1,9 @@ set(LLVM_LINK_COMPONENTS - ${LLVM_TARGETS_TO_BUILD} + AllTargetsAsmPrinters + AllTargetsAsmParsers + AllTargetsDescs + AllTargetsDisassemblers + AllTargetsInfos MC MCParser Support diff --git a/tools/llvm-mc/llvm-mc.cpp b/tools/llvm-mc/llvm-mc.cpp index 78fe9b72f208..6a8b49373df5 100644 --- a/tools/llvm-mc/llvm-mc.cpp +++ b/tools/llvm-mc/llvm-mc.cpp @@ -357,6 +357,7 @@ int main(int argc, char **argv) { cl::ParseCommandLineOptions(argc, argv, "llvm machine code playground\n"); MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags(); TripleName = Triple::normalize(TripleName); + Triple TheTriple(TripleName); setDwarfDebugFlags(argc, argv); setDwarfDebugProducer(); @@ -438,7 +439,8 @@ int main(int argc, char **argv) { if (!Out) return 1; - formatted_raw_ostream FOS(Out->os()); + std::unique_ptr<buffer_ostream> BOS; + raw_pwrite_stream *OS = &Out->os(); std::unique_ptr<MCStreamer> Str; std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo()); @@ -447,8 +449,8 @@ int main(int argc, char **argv) { MCInstPrinter *IP = nullptr; if (FileType == OFT_AssemblyFile) { - IP = - TheTarget->createMCInstPrinter(OutputAsmVariant, *MAI, *MCII, *MRI, *STI); + IP = TheTarget->createMCInstPrinter(Triple(TripleName), OutputAsmVariant, + *MAI, *MCII, *MRI); // Set the display preference for hex vs. decimal immediates. IP->setPrintImmHex(PrintImmHex); @@ -457,21 +459,29 @@ int main(int argc, char **argv) { MCCodeEmitter *CE = nullptr; MCAsmBackend *MAB = nullptr; if (ShowEncoding) { - CE = TheTarget->createMCCodeEmitter(*MCII, *MRI, *STI, Ctx); + CE = TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx); MAB = TheTarget->createMCAsmBackend(*MRI, TripleName, MCPU); } - Str.reset(TheTarget->createAsmStreamer(Ctx, FOS, /*asmverbose*/ true, - /*useDwarfDirectory*/ true, IP, CE, - MAB, ShowInst)); + auto FOut = llvm::make_unique<formatted_raw_ostream>(*OS); + Str.reset(TheTarget->createAsmStreamer( + Ctx, std::move(FOut), /*asmverbose*/ true, + /*useDwarfDirectory*/ true, IP, CE, MAB, ShowInst)); } else if (FileType == OFT_Null) { - Str.reset(createNullStreamer(Ctx)); + Str.reset(TheTarget->createNullStreamer(Ctx)); } else { assert(FileType == OFT_ObjectFile && "Invalid file type!"); - MCCodeEmitter *CE = TheTarget->createMCCodeEmitter(*MCII, *MRI, *STI, Ctx); + + if (!Out->os().supportsSeeking()) { + BOS = make_unique<buffer_ostream>(Out->os()); + OS = BOS.get(); + } + + MCCodeEmitter *CE = TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx); MCAsmBackend *MAB = TheTarget->createMCAsmBackend(*MRI, TripleName, MCPU); - Str.reset(TheTarget->createMCObjectStreamer(TripleName, Ctx, *MAB, FOS, CE, - *STI, RelaxAll)); + Str.reset(TheTarget->createMCObjectStreamer(TheTriple, Ctx, *MAB, *OS, CE, + *STI, RelaxAll, + /*DWARFMustBeAtTheEnd*/ false)); if (NoExecStack) Str->InitSections(true); } diff --git a/tools/llvm-nm/llvm-nm.cpp b/tools/llvm-nm/llvm-nm.cpp index f911c724646c..f3197bbfd104 100644 --- a/tools/llvm-nm/llvm-nm.cpp +++ b/tools/llvm-nm/llvm-nm.cpp @@ -188,85 +188,77 @@ static bool compareSymbolAddress(const NMSymbol &A, const NMSymbol &B) { if (!ReverseSort) { if (A.Address < B.Address) return true; - else if (A.Address == B.Address && A.Name < B.Name) + if (A.Address == B.Address && A.Name < B.Name) return true; - else if (A.Address == B.Address && A.Name == B.Name && A.Size < B.Size) + if (A.Address == B.Address && A.Name == B.Name && A.Size < B.Size) return true; - else - return false; - } else { - if (A.Address > B.Address) - return true; - else if (A.Address == B.Address && A.Name > B.Name) - return true; - else if (A.Address == B.Address && A.Name == B.Name && A.Size > B.Size) - return true; - else - return false; + return false; } + + if (A.Address > B.Address) + return true; + if (A.Address == B.Address && A.Name > B.Name) + return true; + if (A.Address == B.Address && A.Name == B.Name && A.Size > B.Size) + return true; + return false; } static bool compareSymbolSize(const NMSymbol &A, const NMSymbol &B) { if (!ReverseSort) { if (A.Size < B.Size) return true; - else if (A.Size == B.Size && A.Name < B.Name) - return true; - else if (A.Size == B.Size && A.Name == B.Name && A.Address < B.Address) - return true; - else - return false; - } else { - if (A.Size > B.Size) + if (A.Size == B.Size && A.Name < B.Name) return true; - else if (A.Size == B.Size && A.Name > B.Name) + if (A.Size == B.Size && A.Name == B.Name && A.Address < B.Address) return true; - else if (A.Size == B.Size && A.Name == B.Name && A.Address > B.Address) - return true; - else - return false; + return false; } + + if (A.Size > B.Size) + return true; + if (A.Size == B.Size && A.Name > B.Name) + return true; + if (A.Size == B.Size && A.Name == B.Name && A.Address > B.Address) + return true; + return false; } static bool compareSymbolName(const NMSymbol &A, const NMSymbol &B) { if (!ReverseSort) { if (A.Name < B.Name) return true; - else if (A.Name == B.Name && A.Size < B.Size) - return true; - else if (A.Name == B.Name && A.Size == B.Size && A.Address < B.Address) - return true; - else - return false; - } else { - if (A.Name > B.Name) + if (A.Name == B.Name && A.Size < B.Size) return true; - else if (A.Name == B.Name && A.Size > B.Size) + if (A.Name == B.Name && A.Size == B.Size && A.Address < B.Address) return true; - else if (A.Name == B.Name && A.Size == B.Size && A.Address > B.Address) - return true; - else - return false; + return false; } + if (A.Name > B.Name) + return true; + if (A.Name == B.Name && A.Size > B.Size) + return true; + if (A.Name == B.Name && A.Size == B.Size && A.Address > B.Address) + return true; + return false; } static char isSymbolList64Bit(SymbolicFile &Obj) { if (isa<IRObjectFile>(Obj)) return false; - else if (isa<COFFObjectFile>(Obj)) + if (isa<COFFObjectFile>(Obj)) return false; - else if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj)) + if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj)) return MachO->is64Bit(); - else if (isa<ELF32LEObjectFile>(Obj)) + if (isa<ELF32LEObjectFile>(Obj)) return false; - else if (isa<ELF64LEObjectFile>(Obj)) + if (isa<ELF64LEObjectFile>(Obj)) return true; - else if (isa<ELF32BEObjectFile>(Obj)) + if (isa<ELF32BEObjectFile>(Obj)) return false; - else if (isa<ELF64BEObjectFile>(Obj)) + if (isa<ELF64BEObjectFile>(Obj)) return true; - else - return false; + return false; } static StringRef CurrentFilename; @@ -940,7 +932,7 @@ static void dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, NMSymbol S; S.Size = UnknownAddressOrSize; S.Address = UnknownAddressOrSize; - if ((PrintSize || SizeSort) && isa<ObjectFile>(Obj)) { + if (PrintSize && isa<ELFObjectFileBase>(Obj)) { symbol_iterator SymI = I; if (error(SymI->getSize(S.Size))) break; @@ -973,30 +965,26 @@ static void dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, // architectures was specificed. If not then an error is generated and this // routine returns false. Else it returns true. static bool checkMachOAndArchFlags(SymbolicFile *O, std::string &Filename) { - if (isa<MachOObjectFile>(O) && !ArchAll && ArchFlags.size() != 0) { - MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(O); - bool ArchFound = false; - MachO::mach_header H; - MachO::mach_header_64 H_64; - Triple T; - if (MachO->is64Bit()) { - H_64 = MachO->MachOObjectFile::getHeader64(); - T = MachOObjectFile::getArch(H_64.cputype, H_64.cpusubtype); - } else { - H = MachO->MachOObjectFile::getHeader(); - T = MachOObjectFile::getArch(H.cputype, H.cpusubtype); - } - unsigned i; - for (i = 0; i < ArchFlags.size(); ++i) { - if (ArchFlags[i] == T.getArchName()) - ArchFound = true; - break; - } - if (!ArchFound) { - error(ArchFlags[i], - "file: " + Filename + " does not contain architecture"); - return false; - } + MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(O); + + if (!MachO || ArchAll || ArchFlags.size() == 0) + return true; + + MachO::mach_header H; + MachO::mach_header_64 H_64; + Triple T; + if (MachO->is64Bit()) { + H_64 = MachO->MachOObjectFile::getHeader64(); + T = MachOObjectFile::getArch(H_64.cputype, H_64.cpusubtype); + } else { + H = MachO->MachOObjectFile::getHeader(); + T = MachOObjectFile::getArch(H.cputype, H.cpusubtype); + } + if (std::none_of( + ArchFlags.begin(), ArchFlags.end(), + [&](const std::string &Name) { return Name == T.getArchName(); })) { + error("No architecture specified", Filename); + return false; } return true; } diff --git a/tools/llvm-objdump/CMakeLists.txt b/tools/llvm-objdump/CMakeLists.txt index 61bf3b32757b..d717653685c8 100644 --- a/tools/llvm-objdump/CMakeLists.txt +++ b/tools/llvm-objdump/CMakeLists.txt @@ -1,6 +1,6 @@ set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} - DebugInfo + DebugInfoDWARF MC MCDisassembler Object diff --git a/tools/llvm-objdump/LLVMBuild.txt b/tools/llvm-objdump/LLVMBuild.txt index d16c501a6cca..42328737a7d0 100644 --- a/tools/llvm-objdump/LLVMBuild.txt +++ b/tools/llvm-objdump/LLVMBuild.txt @@ -19,4 +19,4 @@ type = Tool name = llvm-objdump parent = Tools -required_libraries = DebugInfo MC MCDisassembler MCParser Object all-targets +required_libraries = DebugInfoDWARF MC MCDisassembler MCParser Object all-targets diff --git a/tools/llvm-objdump/MachODump.cpp b/tools/llvm-objdump/MachODump.cpp index 03fad5f922fc..84212c94bbb2 100644 --- a/tools/llvm-objdump/MachODump.cpp +++ b/tools/llvm-objdump/MachODump.cpp @@ -18,6 +18,7 @@ #include "llvm/ADT/Triple.h" #include "llvm/Config/config.h" #include "llvm/DebugInfo/DIContext.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDisassembler.h" @@ -36,6 +37,7 @@ #include "llvm/Support/Format.h" #include "llvm/Support/FormattedStream.h" #include "llvm/Support/GraphWriter.h" +#include "llvm/Support/LEB128.h" #include "llvm/Support/MachO.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/TargetRegistry.h" @@ -62,13 +64,83 @@ static cl::opt<std::string> DSYMFile("dsym", static cl::opt<bool> FullLeadingAddr("full-leading-addr", cl::desc("Print full leading address")); +static cl::opt<bool> NoLeadingAddr("no-leading-addr", + cl::desc("Print no leading address")); + static cl::opt<bool> PrintImmHex("print-imm-hex", cl::desc("Use hex format for immediate values")); +cl::opt<bool> llvm::UniversalHeaders("universal-headers", + cl::desc("Print Mach-O universal headers " + "(requires -macho)")); + +cl::opt<bool> + llvm::ArchiveHeaders("archive-headers", + cl::desc("Print archive headers for Mach-O archives " + "(requires -macho)")); + +cl::opt<bool> + ArchiveMemberOffsets("archive-member-offsets", + cl::desc("Print the offset to each archive member for " + "Mach-O archives (requires -macho and " + "-archive-headers)")); + +cl::opt<bool> + llvm::IndirectSymbols("indirect-symbols", + cl::desc("Print indirect symbol table for Mach-O " + "objects (requires -macho)")); + +cl::opt<bool> + llvm::DataInCode("data-in-code", + cl::desc("Print the data in code table for Mach-O objects " + "(requires -macho)")); + +cl::opt<bool> + llvm::LinkOptHints("link-opt-hints", + cl::desc("Print the linker optimization hints for " + "Mach-O objects (requires -macho)")); + +cl::list<std::string> + llvm::DumpSections("section", + cl::desc("Prints the specified segment,section for " + "Mach-O objects (requires -macho)")); + +cl::opt<bool> llvm::Raw("raw", + cl::desc("Have -section dump the raw binary contents")); + +cl::opt<bool> + llvm::InfoPlist("info-plist", + cl::desc("Print the info plist section as strings for " + "Mach-O objects (requires -macho)")); + +cl::opt<bool> + llvm::DylibsUsed("dylibs-used", + cl::desc("Print the shared libraries used for linked " + "Mach-O files (requires -macho)")); + +cl::opt<bool> + llvm::DylibId("dylib-id", + cl::desc("Print the shared library's id for the dylib Mach-O " + "file (requires -macho)")); + +cl::opt<bool> + llvm::NonVerbose("non-verbose", + cl::desc("Print the info for Mach-O objects in " + "non-verbose or numeric form (requires -macho)")); + cl::opt<bool> - llvm::UniversalHeaders("universal-headers", - cl::desc("Print Mach-O universal headers")); + llvm::ObjcMetaData("objc-meta-data", + cl::desc("Print the Objective-C runtime meta data for " + "Mach-O files (requires -macho)")); + +cl::opt<std::string> llvm::DisSymName( + "dis-symname", + cl::desc("disassemble just this symbol's instructions (requires -macho")); + +static cl::opt<bool> NoSymbolicOperands( + "no-symbolic-operands", + cl::desc("do not symbolic operands when disassembling (requires -macho)")); static cl::list<std::string> ArchFlags("arch", cl::desc("architecture(s) from a Mach-O file to dump"), @@ -147,7 +219,7 @@ static bool compareDiceTableEntries(const DiceTableEntry &i, return j.first >= i.first && j.first < i.first + Length; } -static uint64_t DumpDataInCode(const char *bytes, uint64_t Length, +static uint64_t DumpDataInCode(const uint8_t *bytes, uint64_t Length, unsigned short Kind) { uint32_t Value, Size = 1; @@ -156,19 +228,19 @@ static uint64_t DumpDataInCode(const char *bytes, uint64_t Length, case MachO::DICE_KIND_DATA: if (Length >= 4) { if (!NoShowRawInsn) - DumpBytes(StringRef(bytes, 4)); + DumpBytes(ArrayRef<uint8_t>(bytes, 4)); 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(StringRef(bytes, 2)); + DumpBytes(ArrayRef<uint8_t>(bytes, 2)); Value = bytes[1] << 8 | bytes[0]; outs() << "\t.short " << Value; Size = 2; } else { if (!NoShowRawInsn) - DumpBytes(StringRef(bytes, 2)); + DumpBytes(ArrayRef<uint8_t>(bytes, 2)); Value = bytes[0]; outs() << "\t.byte " << Value; Size = 1; @@ -180,14 +252,14 @@ static uint64_t DumpDataInCode(const char *bytes, uint64_t Length, break; case MachO::DICE_KIND_JUMP_TABLE8: if (!NoShowRawInsn) - DumpBytes(StringRef(bytes, 1)); + DumpBytes(ArrayRef<uint8_t>(bytes, 1)); 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(StringRef(bytes, 2)); + DumpBytes(ArrayRef<uint8_t>(bytes, 2)); Value = bytes[1] << 8 | bytes[0]; outs() << "\t.short " << format("%5u", Value & 0xffff) << "\t@ KIND_JUMP_TABLE16\n"; @@ -196,7 +268,7 @@ static uint64_t DumpDataInCode(const char *bytes, uint64_t Length, case MachO::DICE_KIND_JUMP_TABLE32: case MachO::DICE_KIND_ABS_JUMP_TABLE32: if (!NoShowRawInsn) - DumpBytes(StringRef(bytes, 4)); + DumpBytes(ArrayRef<uint8_t>(bytes, 4)); Value = bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0]; outs() << "\t.long " << Value; if (Kind == MachO::DICE_KIND_JUMP_TABLE32) @@ -255,6 +327,842 @@ static void getSectionsAndSymbols(const MachO::mach_header Header, } } +static void PrintIndirectSymbolTable(MachOObjectFile *O, bool verbose, + uint32_t n, uint32_t count, + uint32_t stride, uint64_t addr) { + MachO::dysymtab_command Dysymtab = O->getDysymtabLoadCommand(); + uint32_t nindirectsyms = Dysymtab.nindirectsyms; + if (n > nindirectsyms) + outs() << " (entries start past the end of the indirect symbol " + "table) (reserved1 field greater than the table size)"; + else if (n + count > nindirectsyms) + outs() << " (entries extends past the end of the indirect symbol " + "table)"; + outs() << "\n"; + uint32_t cputype = O->getHeader().cputype; + if (cputype & MachO::CPU_ARCH_ABI64) + outs() << "address index"; + else + outs() << "address index"; + if (verbose) + outs() << " name\n"; + else + outs() << "\n"; + for (uint32_t j = 0; j < count && n + j < nindirectsyms; j++) { + if (cputype & MachO::CPU_ARCH_ABI64) + outs() << format("0x%016" PRIx64, addr + j * stride) << " "; + else + outs() << format("0x%08" PRIx32, addr + j * stride) << " "; + MachO::dysymtab_command Dysymtab = O->getDysymtabLoadCommand(); + uint32_t indirect_symbol = O->getIndirectSymbolTableEntry(Dysymtab, n + j); + if (indirect_symbol == MachO::INDIRECT_SYMBOL_LOCAL) { + outs() << "LOCAL\n"; + continue; + } + if (indirect_symbol == + (MachO::INDIRECT_SYMBOL_LOCAL | MachO::INDIRECT_SYMBOL_ABS)) { + outs() << "LOCAL ABSOLUTE\n"; + continue; + } + if (indirect_symbol == MachO::INDIRECT_SYMBOL_ABS) { + outs() << "ABSOLUTE\n"; + continue; + } + outs() << format("%5u ", indirect_symbol); + if (verbose) { + MachO::symtab_command Symtab = O->getSymtabLoadCommand(); + if (indirect_symbol < Symtab.nsyms) { + symbol_iterator Sym = O->getSymbolByIndex(indirect_symbol); + SymbolRef Symbol = *Sym; + StringRef SymName; + Symbol.getName(SymName); + outs() << SymName; + } else { + outs() << "?"; + } + } + outs() << "\n"; + } +} + +static void PrintIndirectSymbols(MachOObjectFile *O, bool verbose) { + uint32_t LoadCommandCount = O->getHeader().ncmds; + MachOObjectFile::LoadCommandInfo Load = O->getFirstLoadCommandInfo(); + for (unsigned I = 0;; ++I) { + if (Load.C.cmd == MachO::LC_SEGMENT_64) { + MachO::segment_command_64 Seg = O->getSegment64LoadCommand(Load); + for (unsigned J = 0; J < Seg.nsects; ++J) { + MachO::section_64 Sec = O->getSection64(Load, J); + uint32_t section_type = Sec.flags & MachO::SECTION_TYPE; + if (section_type == MachO::S_NON_LAZY_SYMBOL_POINTERS || + section_type == MachO::S_LAZY_SYMBOL_POINTERS || + section_type == MachO::S_LAZY_DYLIB_SYMBOL_POINTERS || + section_type == MachO::S_THREAD_LOCAL_VARIABLE_POINTERS || + section_type == MachO::S_SYMBOL_STUBS) { + uint32_t stride; + if (section_type == MachO::S_SYMBOL_STUBS) + stride = Sec.reserved2; + else + stride = 8; + if (stride == 0) { + outs() << "Can't print indirect symbols for (" << Sec.segname << "," + << Sec.sectname << ") " + << "(size of stubs in reserved2 field is zero)\n"; + continue; + } + uint32_t count = Sec.size / stride; + outs() << "Indirect symbols for (" << Sec.segname << "," + << Sec.sectname << ") " << count << " entries"; + uint32_t n = Sec.reserved1; + PrintIndirectSymbolTable(O, verbose, n, count, stride, Sec.addr); + } + } + } else if (Load.C.cmd == MachO::LC_SEGMENT) { + MachO::segment_command Seg = O->getSegmentLoadCommand(Load); + for (unsigned J = 0; J < Seg.nsects; ++J) { + MachO::section Sec = O->getSection(Load, J); + uint32_t section_type = Sec.flags & MachO::SECTION_TYPE; + if (section_type == MachO::S_NON_LAZY_SYMBOL_POINTERS || + section_type == MachO::S_LAZY_SYMBOL_POINTERS || + section_type == MachO::S_LAZY_DYLIB_SYMBOL_POINTERS || + section_type == MachO::S_THREAD_LOCAL_VARIABLE_POINTERS || + section_type == MachO::S_SYMBOL_STUBS) { + uint32_t stride; + if (section_type == MachO::S_SYMBOL_STUBS) + stride = Sec.reserved2; + else + stride = 4; + if (stride == 0) { + outs() << "Can't print indirect symbols for (" << Sec.segname << "," + << Sec.sectname << ") " + << "(size of stubs in reserved2 field is zero)\n"; + continue; + } + uint32_t count = Sec.size / stride; + outs() << "Indirect symbols for (" << Sec.segname << "," + << Sec.sectname << ") " << count << " entries"; + uint32_t n = Sec.reserved1; + PrintIndirectSymbolTable(O, verbose, n, count, stride, Sec.addr); + } + } + } + if (I == LoadCommandCount - 1) + break; + else + Load = O->getNextLoadCommandInfo(Load); + } +} + +static void PrintDataInCodeTable(MachOObjectFile *O, bool verbose) { + MachO::linkedit_data_command DIC = O->getDataInCodeLoadCommand(); + uint32_t nentries = DIC.datasize / sizeof(struct MachO::data_in_code_entry); + outs() << "Data in code table (" << nentries << " entries)\n"; + outs() << "offset length kind\n"; + for (dice_iterator DI = O->begin_dices(), DE = O->end_dices(); DI != DE; + ++DI) { + uint32_t Offset; + DI->getOffset(Offset); + outs() << format("0x%08" PRIx32, Offset) << " "; + uint16_t Length; + DI->getLength(Length); + outs() << format("%6u", Length) << " "; + uint16_t Kind; + DI->getKind(Kind); + if (verbose) { + switch (Kind) { + case MachO::DICE_KIND_DATA: + outs() << "DATA"; + break; + case MachO::DICE_KIND_JUMP_TABLE8: + outs() << "JUMP_TABLE8"; + break; + case MachO::DICE_KIND_JUMP_TABLE16: + outs() << "JUMP_TABLE16"; + break; + case MachO::DICE_KIND_JUMP_TABLE32: + outs() << "JUMP_TABLE32"; + break; + case MachO::DICE_KIND_ABS_JUMP_TABLE32: + outs() << "ABS_JUMP_TABLE32"; + break; + default: + outs() << format("0x%04" PRIx32, Kind); + break; + } + } else + outs() << format("0x%04" PRIx32, Kind); + outs() << "\n"; + } +} + +static void PrintLinkOptHints(MachOObjectFile *O) { + MachO::linkedit_data_command LohLC = O->getLinkOptHintsLoadCommand(); + const char *loh = O->getData().substr(LohLC.dataoff, 1).data(); + uint32_t nloh = LohLC.datasize; + outs() << "Linker optimiztion hints (" << nloh << " total bytes)\n"; + for (uint32_t i = 0; i < nloh;) { + unsigned n; + uint64_t identifier = decodeULEB128((const uint8_t *)(loh + i), &n); + i += n; + outs() << " identifier " << identifier << " "; + if (i >= nloh) + return; + switch (identifier) { + case 1: + outs() << "AdrpAdrp\n"; + break; + case 2: + outs() << "AdrpLdr\n"; + break; + case 3: + outs() << "AdrpAddLdr\n"; + break; + case 4: + outs() << "AdrpLdrGotLdr\n"; + break; + case 5: + outs() << "AdrpAddStr\n"; + break; + case 6: + outs() << "AdrpLdrGotStr\n"; + break; + case 7: + outs() << "AdrpAdd\n"; + break; + case 8: + outs() << "AdrpLdrGot\n"; + break; + default: + outs() << "Unknown identifier value\n"; + break; + } + uint64_t narguments = decodeULEB128((const uint8_t *)(loh + i), &n); + i += n; + outs() << " narguments " << narguments << "\n"; + if (i >= nloh) + return; + + for (uint32_t j = 0; j < narguments; j++) { + uint64_t value = decodeULEB128((const uint8_t *)(loh + i), &n); + i += n; + outs() << "\tvalue " << format("0x%" PRIx64, value) << "\n"; + if (i >= nloh) + return; + } + } +} + +static void PrintDylibs(MachOObjectFile *O, bool JustId) { + uint32_t LoadCommandCount = O->getHeader().ncmds; + MachOObjectFile::LoadCommandInfo Load = O->getFirstLoadCommandInfo(); + for (unsigned I = 0;; ++I) { + if ((JustId && Load.C.cmd == MachO::LC_ID_DYLIB) || + (!JustId && (Load.C.cmd == MachO::LC_ID_DYLIB || + Load.C.cmd == MachO::LC_LOAD_DYLIB || + Load.C.cmd == MachO::LC_LOAD_WEAK_DYLIB || + Load.C.cmd == MachO::LC_REEXPORT_DYLIB || + Load.C.cmd == MachO::LC_LAZY_LOAD_DYLIB || + Load.C.cmd == MachO::LC_LOAD_UPWARD_DYLIB))) { + MachO::dylib_command dl = O->getDylibIDLoadCommand(Load); + if (dl.dylib.name < dl.cmdsize) { + const char *p = (const char *)(Load.Ptr) + dl.dylib.name; + if (JustId) + outs() << p << "\n"; + else { + outs() << "\t" << p; + outs() << " (compatibility version " + << ((dl.dylib.compatibility_version >> 16) & 0xffff) << "." + << ((dl.dylib.compatibility_version >> 8) & 0xff) << "." + << (dl.dylib.compatibility_version & 0xff) << ","; + outs() << " current version " + << ((dl.dylib.current_version >> 16) & 0xffff) << "." + << ((dl.dylib.current_version >> 8) & 0xff) << "." + << (dl.dylib.current_version & 0xff) << ")\n"; + } + } else { + outs() << "\tBad offset (" << dl.dylib.name << ") for name of "; + if (Load.C.cmd == MachO::LC_ID_DYLIB) + outs() << "LC_ID_DYLIB "; + else if (Load.C.cmd == MachO::LC_LOAD_DYLIB) + outs() << "LC_LOAD_DYLIB "; + else if (Load.C.cmd == MachO::LC_LOAD_WEAK_DYLIB) + outs() << "LC_LOAD_WEAK_DYLIB "; + else if (Load.C.cmd == MachO::LC_LAZY_LOAD_DYLIB) + outs() << "LC_LAZY_LOAD_DYLIB "; + else if (Load.C.cmd == MachO::LC_REEXPORT_DYLIB) + outs() << "LC_REEXPORT_DYLIB "; + else if (Load.C.cmd == MachO::LC_LOAD_UPWARD_DYLIB) + outs() << "LC_LOAD_UPWARD_DYLIB "; + else + outs() << "LC_??? "; + outs() << "command " << I << "\n"; + } + } + if (I == LoadCommandCount - 1) + break; + else + Load = O->getNextLoadCommandInfo(Load); + } +} + +typedef DenseMap<uint64_t, StringRef> SymbolAddressMap; + +static void CreateSymbolAddressMap(MachOObjectFile *O, + SymbolAddressMap *AddrMap) { + // Create a map of symbol addresses to symbol names. + for (const SymbolRef &Symbol : O->symbols()) { + SymbolRef::Type ST; + Symbol.getType(ST); + if (ST == SymbolRef::ST_Function || ST == SymbolRef::ST_Data || + ST == SymbolRef::ST_Other) { + uint64_t Address; + Symbol.getAddress(Address); + StringRef SymName; + Symbol.getName(SymName); + if (!SymName.startswith(".objc")) + (*AddrMap)[Address] = SymName; + } + } +} + +// GuessSymbolName is passed the address of what might be a symbol and a +// pointer to the SymbolAddressMap. It returns the name of a symbol +// with that address or nullptr if no symbol is found with that address. +static const char *GuessSymbolName(uint64_t value, SymbolAddressMap *AddrMap) { + const char *SymbolName = nullptr; + // A DenseMap can't lookup up some values. + if (value != 0xffffffffffffffffULL && value != 0xfffffffffffffffeULL) { + StringRef name = AddrMap->lookup(value); + if (!name.empty()) + SymbolName = name.data(); + } + return SymbolName; +} + +static void DumpCstringChar(const char c) { + char p[2]; + p[0] = c; + p[1] = '\0'; + outs().write_escaped(p); +} + +static void DumpCstringSection(MachOObjectFile *O, const char *sect, + uint32_t sect_size, uint64_t sect_addr, + bool print_addresses) { + for (uint32_t i = 0; i < sect_size; i++) { + if (print_addresses) { + if (O->is64Bit()) + outs() << format("%016" PRIx64, sect_addr + i) << " "; + else + outs() << format("%08" PRIx64, sect_addr + i) << " "; + } + for (; i < sect_size && sect[i] != '\0'; i++) + DumpCstringChar(sect[i]); + if (i < sect_size && sect[i] == '\0') + outs() << "\n"; + } +} + +static void DumpLiteral4(uint32_t l, float f) { + outs() << format("0x%08" PRIx32, l); + if ((l & 0x7f800000) != 0x7f800000) + outs() << format(" (%.16e)\n", f); + else { + if (l == 0x7f800000) + outs() << " (+Infinity)\n"; + else if (l == 0xff800000) + outs() << " (-Infinity)\n"; + else if ((l & 0x00400000) == 0x00400000) + outs() << " (non-signaling Not-a-Number)\n"; + else + outs() << " (signaling Not-a-Number)\n"; + } +} + +static void DumpLiteral4Section(MachOObjectFile *O, const char *sect, + uint32_t sect_size, uint64_t sect_addr, + bool print_addresses) { + for (uint32_t i = 0; i < sect_size; i += sizeof(float)) { + if (print_addresses) { + if (O->is64Bit()) + outs() << format("%016" PRIx64, sect_addr + i) << " "; + else + outs() << format("%08" PRIx64, sect_addr + i) << " "; + } + float f; + memcpy(&f, sect + i, sizeof(float)); + if (O->isLittleEndian() != sys::IsLittleEndianHost) + sys::swapByteOrder(f); + uint32_t l; + memcpy(&l, sect + i, sizeof(uint32_t)); + if (O->isLittleEndian() != sys::IsLittleEndianHost) + sys::swapByteOrder(l); + DumpLiteral4(l, f); + } +} + +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 is the high word, so this is equivalent to if(isfinite(d)) + if ((Hi & 0x7ff00000) != 0x7ff00000) + outs() << format(" (%.16e)\n", d); + else { + if (Hi == 0x7ff00000 && Lo == 0) + outs() << " (+Infinity)\n"; + else if (Hi == 0xfff00000 && Lo == 0) + outs() << " (-Infinity)\n"; + else if ((Hi & 0x00080000) == 0x00080000) + outs() << " (non-signaling Not-a-Number)\n"; + else + outs() << " (signaling Not-a-Number)\n"; + } +} + +static void DumpLiteral8Section(MachOObjectFile *O, const char *sect, + uint32_t sect_size, uint64_t sect_addr, + bool print_addresses) { + for (uint32_t i = 0; i < sect_size; i += sizeof(double)) { + if (print_addresses) { + if (O->is64Bit()) + outs() << format("%016" PRIx64, sect_addr + i) << " "; + else + outs() << format("%08" PRIx64, sect_addr + i) << " "; + } + double d; + memcpy(&d, sect + i, sizeof(double)); + if (O->isLittleEndian() != sys::IsLittleEndianHost) + sys::swapByteOrder(d); + uint32_t l0, l1; + memcpy(&l0, sect + i, sizeof(uint32_t)); + memcpy(&l1, sect + i + sizeof(uint32_t), sizeof(uint32_t)); + if (O->isLittleEndian() != sys::IsLittleEndianHost) { + sys::swapByteOrder(l0); + sys::swapByteOrder(l1); + } + DumpLiteral8(O, l0, l1, d); + } +} + +static void DumpLiteral16(uint32_t l0, uint32_t l1, uint32_t l2, uint32_t l3) { + outs() << format("0x%08" PRIx32, l0) << " "; + outs() << format("0x%08" PRIx32, l1) << " "; + outs() << format("0x%08" PRIx32, l2) << " "; + outs() << format("0x%08" PRIx32, l3) << "\n"; +} + +static void DumpLiteral16Section(MachOObjectFile *O, const char *sect, + uint32_t sect_size, uint64_t sect_addr, + bool print_addresses) { + for (uint32_t i = 0; i < sect_size; i += 16) { + if (print_addresses) { + if (O->is64Bit()) + outs() << format("%016" PRIx64, sect_addr + i) << " "; + else + outs() << format("%08" PRIx64, sect_addr + i) << " "; + } + uint32_t l0, l1, l2, l3; + memcpy(&l0, sect + i, sizeof(uint32_t)); + memcpy(&l1, sect + i + sizeof(uint32_t), sizeof(uint32_t)); + memcpy(&l2, sect + i + 2 * sizeof(uint32_t), sizeof(uint32_t)); + memcpy(&l3, sect + i + 3 * sizeof(uint32_t), sizeof(uint32_t)); + if (O->isLittleEndian() != sys::IsLittleEndianHost) { + sys::swapByteOrder(l0); + sys::swapByteOrder(l1); + sys::swapByteOrder(l2); + sys::swapByteOrder(l3); + } + DumpLiteral16(l0, l1, l2, l3); + } +} + +static void DumpLiteralPointerSection(MachOObjectFile *O, + const SectionRef &Section, + const char *sect, uint32_t sect_size, + uint64_t sect_addr, + bool print_addresses) { + // Collect the literal sections in this Mach-O file. + std::vector<SectionRef> LiteralSections; + for (const SectionRef &Section : O->sections()) { + DataRefImpl Ref = Section.getRawDataRefImpl(); + uint32_t section_type; + if (O->is64Bit()) { + const MachO::section_64 Sec = O->getSection64(Ref); + section_type = Sec.flags & MachO::SECTION_TYPE; + } else { + const MachO::section Sec = O->getSection(Ref); + section_type = Sec.flags & MachO::SECTION_TYPE; + } + if (section_type == MachO::S_CSTRING_LITERALS || + section_type == MachO::S_4BYTE_LITERALS || + section_type == MachO::S_8BYTE_LITERALS || + section_type == MachO::S_16BYTE_LITERALS) + LiteralSections.push_back(Section); + } + + // Set the size of the literal pointer. + uint32_t lp_size = O->is64Bit() ? 8 : 4; + + // Collect the external relocation symbols for the the literal pointers. + std::vector<std::pair<uint64_t, SymbolRef>> Relocs; + for (const RelocationRef &Reloc : Section.relocations()) { + DataRefImpl Rel; + MachO::any_relocation_info RE; + bool isExtern = false; + Rel = Reloc.getRawDataRefImpl(); + RE = O->getRelocation(Rel); + isExtern = O->getPlainRelocationExternal(RE); + if (isExtern) { + uint64_t RelocOffset; + Reloc.getOffset(RelocOffset); + symbol_iterator RelocSym = Reloc.getSymbol(); + Relocs.push_back(std::make_pair(RelocOffset, *RelocSym)); + } + } + array_pod_sort(Relocs.begin(), Relocs.end()); + + // Dump each literal pointer. + for (uint32_t i = 0; i < sect_size; i += lp_size) { + if (print_addresses) { + if (O->is64Bit()) + outs() << format("%016" PRIx64, sect_addr + i) << " "; + else + outs() << format("%08" PRIx64, sect_addr + i) << " "; + } + uint64_t lp; + if (O->is64Bit()) { + memcpy(&lp, sect + i, sizeof(uint64_t)); + if (O->isLittleEndian() != sys::IsLittleEndianHost) + sys::swapByteOrder(lp); + } else { + uint32_t li; + memcpy(&li, sect + i, sizeof(uint32_t)); + if (O->isLittleEndian() != sys::IsLittleEndianHost) + sys::swapByteOrder(li); + lp = li; + } + + // First look for an external relocation entry for this literal pointer. + auto Reloc = std::find_if( + Relocs.begin(), Relocs.end(), + [&](const std::pair<uint64_t, SymbolRef> &P) { return P.first == i; }); + if (Reloc != Relocs.end()) { + symbol_iterator RelocSym = Reloc->second; + StringRef SymName; + RelocSym->getName(SymName); + outs() << "external relocation entry for symbol:" << SymName << "\n"; + continue; + } + + // For local references see what the section the literal pointer points to. + auto Sect = std::find_if(LiteralSections.begin(), LiteralSections.end(), + [&](const SectionRef &R) { + return lp >= R.getAddress() && + lp < R.getAddress() + R.getSize(); + }); + if (Sect == LiteralSections.end()) { + outs() << format("0x%" PRIx64, lp) << " (not in a literal section)\n"; + continue; + } + + uint64_t SectAddress = Sect->getAddress(); + uint64_t SectSize = Sect->getSize(); + + StringRef SectName; + Sect->getName(SectName); + DataRefImpl Ref = Sect->getRawDataRefImpl(); + StringRef SegmentName = O->getSectionFinalSegmentName(Ref); + outs() << SegmentName << ":" << SectName << ":"; + + uint32_t section_type; + if (O->is64Bit()) { + const MachO::section_64 Sec = O->getSection64(Ref); + section_type = Sec.flags & MachO::SECTION_TYPE; + } else { + const MachO::section Sec = O->getSection(Ref); + section_type = Sec.flags & MachO::SECTION_TYPE; + } + + StringRef BytesStr; + Sect->getContents(BytesStr); + const char *Contents = reinterpret_cast<const char *>(BytesStr.data()); + + switch (section_type) { + case MachO::S_CSTRING_LITERALS: + for (uint64_t i = lp - SectAddress; i < SectSize && Contents[i] != '\0'; + i++) { + DumpCstringChar(Contents[i]); + } + outs() << "\n"; + break; + case MachO::S_4BYTE_LITERALS: + float f; + memcpy(&f, Contents + (lp - SectAddress), sizeof(float)); + uint32_t l; + memcpy(&l, Contents + (lp - SectAddress), sizeof(uint32_t)); + if (O->isLittleEndian() != sys::IsLittleEndianHost) { + sys::swapByteOrder(f); + sys::swapByteOrder(l); + } + DumpLiteral4(l, f); + break; + case MachO::S_8BYTE_LITERALS: { + double d; + memcpy(&d, Contents + (lp - SectAddress), sizeof(double)); + uint32_t l0, l1; + memcpy(&l0, Contents + (lp - SectAddress), sizeof(uint32_t)); + memcpy(&l1, Contents + (lp - SectAddress) + sizeof(uint32_t), + sizeof(uint32_t)); + if (O->isLittleEndian() != sys::IsLittleEndianHost) { + sys::swapByteOrder(f); + sys::swapByteOrder(l0); + sys::swapByteOrder(l1); + } + DumpLiteral8(O, l0, l1, d); + break; + } + case MachO::S_16BYTE_LITERALS: { + uint32_t l0, l1, l2, l3; + memcpy(&l0, Contents + (lp - SectAddress), sizeof(uint32_t)); + memcpy(&l1, Contents + (lp - SectAddress) + sizeof(uint32_t), + sizeof(uint32_t)); + memcpy(&l2, Contents + (lp - SectAddress) + 2 * sizeof(uint32_t), + sizeof(uint32_t)); + memcpy(&l3, Contents + (lp - SectAddress) + 3 * sizeof(uint32_t), + sizeof(uint32_t)); + if (O->isLittleEndian() != sys::IsLittleEndianHost) { + sys::swapByteOrder(l0); + sys::swapByteOrder(l1); + sys::swapByteOrder(l2); + sys::swapByteOrder(l3); + } + DumpLiteral16(l0, l1, l2, l3); + break; + } + } + } +} + +static void DumpInitTermPointerSection(MachOObjectFile *O, const char *sect, + uint32_t sect_size, uint64_t sect_addr, + SymbolAddressMap *AddrMap, + bool verbose) { + uint32_t stride; + if (O->is64Bit()) + stride = sizeof(uint64_t); + else + stride = sizeof(uint32_t); + for (uint32_t i = 0; i < sect_size; i += stride) { + const char *SymbolName = nullptr; + if (O->is64Bit()) { + outs() << format("0x%016" PRIx64, sect_addr + i * stride) << " "; + uint64_t pointer_value; + memcpy(&pointer_value, sect + i, stride); + if (O->isLittleEndian() != sys::IsLittleEndianHost) + sys::swapByteOrder(pointer_value); + outs() << format("0x%016" PRIx64, pointer_value); + if (verbose) + SymbolName = GuessSymbolName(pointer_value, AddrMap); + } else { + outs() << format("0x%08" PRIx64, sect_addr + i * stride) << " "; + uint32_t pointer_value; + memcpy(&pointer_value, sect + i, stride); + if (O->isLittleEndian() != sys::IsLittleEndianHost) + sys::swapByteOrder(pointer_value); + outs() << format("0x%08" PRIx32, pointer_value); + if (verbose) + SymbolName = GuessSymbolName(pointer_value, AddrMap); + } + if (SymbolName) + outs() << " " << SymbolName; + outs() << "\n"; + } +} + +static void DumpRawSectionContents(MachOObjectFile *O, const char *sect, + uint32_t size, uint64_t addr) { + uint32_t cputype = O->getHeader().cputype; + if (cputype == MachO::CPU_TYPE_I386 || cputype == MachO::CPU_TYPE_X86_64) { + uint32_t j; + for (uint32_t i = 0; i < size; i += j, addr += j) { + if (O->is64Bit()) + outs() << format("%016" PRIx64, addr) << "\t"; + else + outs() << format("%08" PRIx64, addr) << "\t"; + for (j = 0; j < 16 && i + j < size; j++) { + uint8_t byte_word = *(sect + i + j); + outs() << format("%02" PRIx32, (uint32_t)byte_word) << " "; + } + outs() << "\n"; + } + } else { + uint32_t j; + for (uint32_t i = 0; i < size; i += j, addr += j) { + if (O->is64Bit()) + outs() << format("%016" PRIx64, addr) << "\t"; + else + outs() << format("%08" PRIx64, sect) << "\t"; + for (j = 0; j < 4 * sizeof(int32_t) && i + j < size; + j += sizeof(int32_t)) { + if (i + j + sizeof(int32_t) < size) { + uint32_t long_word; + memcpy(&long_word, sect + i + j, sizeof(int32_t)); + if (O->isLittleEndian() != sys::IsLittleEndianHost) + sys::swapByteOrder(long_word); + outs() << format("%08" PRIx32, long_word) << " "; + } else { + for (uint32_t k = 0; i + j + k < size; k++) { + uint8_t byte_word = *(sect + i + j); + outs() << format("%02" PRIx32, (uint32_t)byte_word) << " "; + } + } + } + outs() << "\n"; + } + } +} + +static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, + StringRef DisSegName, StringRef DisSectName); +static void DumpProtocolSection(MachOObjectFile *O, const char *sect, + uint32_t size, uint32_t addr); + +static void DumpSectionContents(StringRef Filename, MachOObjectFile *O, + bool verbose) { + SymbolAddressMap AddrMap; + if (verbose) + CreateSymbolAddressMap(O, &AddrMap); + + for (unsigned i = 0; i < DumpSections.size(); ++i) { + StringRef DumpSection = DumpSections[i]; + std::pair<StringRef, StringRef> DumpSegSectName; + DumpSegSectName = DumpSection.split(','); + StringRef DumpSegName, DumpSectName; + if (DumpSegSectName.second.size()) { + DumpSegName = DumpSegSectName.first; + DumpSectName = DumpSegSectName.second; + } else { + DumpSegName = ""; + DumpSectName = DumpSegSectName.first; + } + for (const SectionRef &Section : O->sections()) { + StringRef SectName; + Section.getName(SectName); + DataRefImpl Ref = Section.getRawDataRefImpl(); + StringRef SegName = O->getSectionFinalSegmentName(Ref); + if ((DumpSegName.empty() || SegName == DumpSegName) && + (SectName == DumpSectName)) { + + uint32_t section_flags; + if (O->is64Bit()) { + const MachO::section_64 Sec = O->getSection64(Ref); + section_flags = Sec.flags; + + } else { + const MachO::section Sec = O->getSection(Ref); + section_flags = Sec.flags; + } + uint32_t section_type = section_flags & MachO::SECTION_TYPE; + + StringRef BytesStr; + Section.getContents(BytesStr); + const char *sect = reinterpret_cast<const char *>(BytesStr.data()); + uint32_t sect_size = BytesStr.size(); + uint64_t sect_addr = Section.getAddress(); + + if (Raw) { + outs().write(BytesStr.data(), BytesStr.size()); + continue; + } + + outs() << "Contents of (" << SegName << "," << SectName + << ") section\n"; + + if (verbose) { + if ((section_flags & MachO::S_ATTR_PURE_INSTRUCTIONS) || + (section_flags & MachO::S_ATTR_SOME_INSTRUCTIONS)) { + DisassembleMachO(Filename, O, SegName, SectName); + continue; + } + if (SegName == "__TEXT" && SectName == "__info_plist") { + outs() << sect; + continue; + } + if (SegName == "__OBJC" && SectName == "__protocol") { + DumpProtocolSection(O, sect, sect_size, sect_addr); + continue; + } + switch (section_type) { + case MachO::S_REGULAR: + DumpRawSectionContents(O, sect, sect_size, sect_addr); + break; + case MachO::S_ZEROFILL: + outs() << "zerofill section and has no contents in the file\n"; + break; + case MachO::S_CSTRING_LITERALS: + DumpCstringSection(O, sect, sect_size, sect_addr, !NoLeadingAddr); + break; + case MachO::S_4BYTE_LITERALS: + DumpLiteral4Section(O, sect, sect_size, sect_addr, !NoLeadingAddr); + break; + case MachO::S_8BYTE_LITERALS: + DumpLiteral8Section(O, sect, sect_size, sect_addr, !NoLeadingAddr); + break; + case MachO::S_16BYTE_LITERALS: + DumpLiteral16Section(O, sect, sect_size, sect_addr, !NoLeadingAddr); + break; + case MachO::S_LITERAL_POINTERS: + DumpLiteralPointerSection(O, Section, sect, sect_size, sect_addr, + !NoLeadingAddr); + break; + case MachO::S_MOD_INIT_FUNC_POINTERS: + case MachO::S_MOD_TERM_FUNC_POINTERS: + DumpInitTermPointerSection(O, sect, sect_size, sect_addr, &AddrMap, + verbose); + break; + default: + outs() << "Unknown section type (" + << format("0x%08" PRIx32, section_type) << ")\n"; + DumpRawSectionContents(O, sect, sect_size, sect_addr); + break; + } + } else { + if (section_type == MachO::S_ZEROFILL) + outs() << "zerofill section and has no contents in the file\n"; + else + DumpRawSectionContents(O, sect, sect_size, sect_addr); + } + } + } + } +} + +static void DumpInfoPlistSectionContents(StringRef Filename, + MachOObjectFile *O) { + for (const SectionRef &Section : O->sections()) { + StringRef SectName; + Section.getName(SectName); + DataRefImpl Ref = Section.getRawDataRefImpl(); + StringRef SegName = O->getSectionFinalSegmentName(Ref); + if (SegName == "__TEXT" && SectName == "__info_plist") { + outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; + StringRef BytesStr; + Section.getContents(BytesStr); + const char *sect = reinterpret_cast<const char *>(BytesStr.data()); + outs() << sect; + return; + } + } +} + // checkMachOAndArchFlags() checks to see if the ObjectFile is a Mach-O file // and if it is and there is a list of architecture flags is specified then // check to make sure this Mach-O file is one of those architectures or all @@ -289,7 +1197,7 @@ static bool checkMachOAndArchFlags(ObjectFile *O, StringRef Filename) { return true; } -static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF); +static void printObjcMetaData(MachOObjectFile *O, bool verbose); // ProcessMachO() is passed a single opened Mach-O file, which may be an // archive member and or in a slice of a universal file. It prints the @@ -300,9 +1208,11 @@ static void ProcessMachO(StringRef Filename, MachOObjectFile *MachOOF, StringRef ArchitectureName = StringRef()) { // If we are doing some processing here on the Mach-O file print the header // info. And don't print it otherwise like in the case of printing the - // UniversalHeaders. + // UniversalHeaders or ArchiveHeaders. if (Disassemble || PrivateHeaders || ExportsTrie || Rebase || Bind || - LazyBind || WeakBind) { + LazyBind || WeakBind || IndirectSymbols || DataInCode || LinkOptHints || + DylibsUsed || DylibId || ObjcMetaData || + (DumpSections.size() != 0 && !Raw)) { outs() << Filename; if (!ArchiveMemberName.empty()) outs() << '(' << ArchiveMemberName << ')'; @@ -312,23 +1222,35 @@ static void ProcessMachO(StringRef Filename, MachOObjectFile *MachOOF, } if (Disassemble) - DisassembleMachO(Filename, MachOOF); - // TODO: These should/could be printed in Darwin's otool(1) or nm(1) style - // for -macho. Or just used a new option that maps to the otool(1) - // option like -r, -l, etc. Or just the normal llvm-objdump option - // but now for this slice so that the -arch options can be used. - // if (Relocations) - // PrintRelocations(MachOOF); - // if (SectionHeaders) - // PrintSectionHeaders(MachOOF); - // if (SectionContents) - // PrintSectionContents(MachOOF); - // if (SymbolTable) - // PrintSymbolTable(MachOOF); - // if (UnwindInfo) - // PrintUnwindInfo(MachOOF); + DisassembleMachO(Filename, MachOOF, "__TEXT", "__text"); + if (IndirectSymbols) + PrintIndirectSymbols(MachOOF, !NonVerbose); + if (DataInCode) + PrintDataInCodeTable(MachOOF, !NonVerbose); + if (LinkOptHints) + PrintLinkOptHints(MachOOF); + if (Relocations) + PrintRelocations(MachOOF); + if (SectionHeaders) + PrintSectionHeaders(MachOOF); + if (SectionContents) + PrintSectionContents(MachOOF); + if (DumpSections.size() != 0) + DumpSectionContents(Filename, MachOOF, !NonVerbose); + if (InfoPlist) + DumpInfoPlistSectionContents(Filename, MachOOF); + if (DylibsUsed) + PrintDylibs(MachOOF, false); + if (DylibId) + PrintDylibs(MachOOF, true); + if (SymbolTable) + PrintSymbolTable(MachOOF); + if (UnwindInfo) + printMachOUnwindInfo(MachOOF); if (PrivateHeaders) printMachOFileHeader(MachOOF); + if (ObjcMetaData) + printObjcMetaData(MachOOF, !NonVerbose); if (ExportsTrie) printExportsTrie(MachOOF); if (Rebase) @@ -514,6 +1436,106 @@ static void printMachOUniversalHeaders(const object::MachOUniversalBinary *UB, } } +static void printArchiveChild(Archive::Child &C, bool verbose, + bool print_offset) { + if (print_offset) + outs() << C.getChildOffset() << "\t"; + sys::fs::perms Mode = C.getAccessMode(); + if (verbose) { + // FIXME: this first dash, "-", is for (Mode & S_IFMT) == S_IFREG. + // But there is nothing in sys::fs::perms for S_IFMT or S_IFREG. + 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() << "-"; + } else { + outs() << format("0%o ", Mode); + } + + unsigned UID = C.getUID(); + outs() << format("%3d/", UID); + unsigned GID = C.getGID(); + outs() << format("%-3d ", GID); + uint64_t Size = C.getRawSize(); + outs() << format("%5" PRId64, Size) << " "; + + StringRef RawLastModified = C.getRawLastModified(); + if (verbose) { + unsigned Seconds; + if (RawLastModified.getAsInteger(10, Seconds)) + outs() << "(date: \"%s\" contains non-decimal chars) " << RawLastModified; + else { + // Since cime(3) returns a 26 character string of the form: + // "Sun Sep 16 01:03:52 1973\n\0" + // just print 24 characters. + time_t t = Seconds; + outs() << format("%.24s ", ctime(&t)); + } + } else { + outs() << RawLastModified << " "; + } + + if (verbose) { + ErrorOr<StringRef> NameOrErr = C.getName(); + if (NameOrErr.getError()) { + StringRef RawName = C.getRawName(); + outs() << RawName << "\n"; + } else { + StringRef Name = NameOrErr.get(); + outs() << Name << "\n"; + } + } else { + StringRef RawName = C.getRawName(); + outs() << RawName << "\n"; + } +} + +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; + printArchiveChild(C, verbose, print_offset); + } +} + // ParseInputMachO() parses the named Mach-O file in Filename and handles the // -arch flags selecting just those slices as specified by them and also parses // archive files. Then for each individual Mach-O file ProcessMachO() is @@ -542,6 +1564,8 @@ void llvm::ParseInputMachO(StringRef Filename) { if (Archive *A = dyn_cast<Archive>(&Bin)) { outs() << "Archive : " << Filename << "\n"; + if (ArchiveHeaders) + 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(); @@ -557,7 +1581,7 @@ void llvm::ParseInputMachO(StringRef Filename) { } if (UniversalHeaders) { if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(&Bin)) - printMachOUniversalHeaders(UB, true); + printMachOUniversalHeaders(UB, !NonVerbose); } if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(&Bin)) { // If we have a list of architecture flags specified dump only those. @@ -587,6 +1611,8 @@ void llvm::ParseInputMachO(StringRef Filename) { if (!ArchitectureName.empty()) outs() << " (architecture " << ArchitectureName << ")"; outs() << "\n"; + if (ArchiveHeaders) + printArchiveHeaders(A.get(), !NonVerbose, ArchiveMemberOffsets); for (Archive::child_iterator AI = A->child_begin(), AE = A->child_end(); AI != AE; ++AI) { @@ -627,6 +1653,8 @@ void llvm::ParseInputMachO(StringRef Filename) { I->getAsArchive()) { std::unique_ptr<Archive> &A = *AOrErr; outs() << "Archive : " << Filename << "\n"; + if (ArchiveHeaders) + printArchiveHeaders(A.get(), !NonVerbose, ArchiveMemberOffsets); for (Archive::child_iterator AI = A->child_begin(), AE = A->child_end(); AI != AE; ++AI) { @@ -662,6 +1690,8 @@ void llvm::ParseInputMachO(StringRef Filename) { if (!ArchitectureName.empty()) outs() << " (architecture " << ArchitectureName << ")"; outs() << "\n"; + if (ArchiveHeaders) + 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(); @@ -691,7 +1721,6 @@ void llvm::ParseInputMachO(StringRef Filename) { << "Unrecognized file type.\n"; } -typedef DenseMap<uint64_t, StringRef> SymbolAddressMap; typedef std::pair<uint64_t, const char *> BindInfoEntry; typedef std::vector<BindInfoEntry> BindTable; typedef BindTable::iterator bind_table_iterator; @@ -712,21 +1741,6 @@ struct DisassembleInfo { BindTable *bindtable; }; -// GuessSymbolName is passed the address of what might be a symbol and a -// pointer to the DisassembleInfo struct. It returns the name of a symbol -// with that address or nullptr if no symbol is found with that address. -static const char *GuessSymbolName(uint64_t value, - struct DisassembleInfo *info) { - const char *SymbolName = nullptr; - // A DenseMap can't lookup up some values. - if (value != 0xffffffffffffffffULL && value != 0xfffffffffffffffeULL) { - StringRef name = info->AddrMap->lookup(value); - if (!name.empty()) - SymbolName = name.data(); - } - return SymbolName; -} - // SymbolizerGetOpInfo() is the operand information call back function. // This is called to get the symbolic information for operand(s) of an // instruction when it is being done. This routine does this from @@ -742,8 +1756,8 @@ static const char *GuessSymbolName(uint64_t value, // names and addends of the symbolic expression to add for the operand. The // value of TagType is currently 1 (for the LLVMOpInfo1 struct). If symbolic // information is returned then this function returns 1 else it returns 0. -int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset, - uint64_t Size, int TagType, void *TagBuf) { +static int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset, + uint64_t Size, int TagType, void *TagBuf) { struct DisassembleInfo *info = (struct DisassembleInfo *)DisInfo; struct LLVMOpInfo1 *op_info = (struct LLVMOpInfo1 *)TagBuf; uint64_t value = op_info->Value; @@ -755,7 +1769,7 @@ int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset, // If the TagType is not the value 1 which it code knows about or if no // verbose symbolic information is wanted then just return 0, indicating no // information is being returned. - if (TagType != 1 || info->verbose == false) + if (TagType != 1 || !info->verbose) return 0; unsigned int Arch = info->O->getArch(); @@ -817,8 +1831,8 @@ int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset, } if (reloc_found && (r_type == MachO::GENERIC_RELOC_SECTDIFF || r_type == MachO::GENERIC_RELOC_LOCAL_SECTDIFF)) { - const char *add = GuessSymbolName(r_value, info); - const char *sub = GuessSymbolName(pair_r_value, info); + const char *add = GuessSymbolName(r_value, info->AddrMap); + const char *sub = GuessSymbolName(pair_r_value, info->AddrMap); uint32_t offset = value - (r_value - pair_r_value); op_info->AddSymbol.Present = 1; if (add != nullptr) @@ -838,7 +1852,8 @@ int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset, // (if any) for an entry that matches this segment offset. // uint32_t seg_offset = (Pc + Offset); return 0; - } else if (Arch == Triple::x86_64) { + } + if (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 @@ -904,89 +1919,77 @@ int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset, // (if any) for an entry that matches this segment offset. // uint64_t seg_offset = (Pc + Offset); return 0; - } else if (Arch == Triple::arm) { + } + if (Arch == Triple::arm) { if (Offset != 0 || (Size != 4 && Size != 2)) return 0; // First search the section's relocation entries (if any) for an entry // for this section offset. uint32_t sect_addr = info->S.getAddress(); uint32_t sect_offset = (Pc + Offset) - sect_addr; - bool reloc_found = false; DataRefImpl Rel; MachO::any_relocation_info RE; bool isExtern = false; SymbolRef Symbol; bool r_scattered = false; uint32_t r_value, pair_r_value, r_type, r_length, other_half; - for (const RelocationRef &Reloc : info->S.relocations()) { - uint64_t RelocOffset; - Reloc.getOffset(RelocOffset); - if (RelocOffset == sect_offset) { - Rel = Reloc.getRawDataRefImpl(); - RE = info->O->getRelocation(Rel); - r_length = info->O->getAnyRelocationLength(RE); - r_scattered = info->O->isRelocationScattered(RE); - if (r_scattered) { - r_value = info->O->getScatteredRelocationValue(RE); - r_type = info->O->getScatteredRelocationType(RE); - } else { - r_type = info->O->getAnyRelocationType(RE); - isExtern = info->O->getPlainRelocationExternal(RE); - if (isExtern) { - symbol_iterator RelocSym = Reloc.getSymbol(); - Symbol = *RelocSym; - } - } - if (r_type == MachO::ARM_RELOC_HALF || - r_type == MachO::ARM_RELOC_SECTDIFF || - r_type == MachO::ARM_RELOC_LOCAL_SECTDIFF || - r_type == MachO::ARM_RELOC_HALF_SECTDIFF) { - DataRefImpl RelNext = Rel; - info->O->moveRelocationNext(RelNext); - MachO::any_relocation_info RENext; - RENext = info->O->getRelocation(RelNext); - other_half = info->O->getAnyRelocationAddress(RENext) & 0xffff; - if (info->O->isRelocationScattered(RENext)) - pair_r_value = info->O->getScatteredRelocationValue(RENext); - } - reloc_found = true; - break; + auto Reloc = + std::find_if(info->S.relocations().begin(), info->S.relocations().end(), + [&](const RelocationRef &Reloc) { + uint64_t RelocOffset; + Reloc.getOffset(RelocOffset); + return RelocOffset == sect_offset; + }); + + if (Reloc == info->S.relocations().end()) + return 0; + + Rel = Reloc->getRawDataRefImpl(); + RE = info->O->getRelocation(Rel); + r_length = info->O->getAnyRelocationLength(RE); + r_scattered = info->O->isRelocationScattered(RE); + if (r_scattered) { + r_value = info->O->getScatteredRelocationValue(RE); + r_type = info->O->getScatteredRelocationType(RE); + } else { + r_type = info->O->getAnyRelocationType(RE); + isExtern = info->O->getPlainRelocationExternal(RE); + if (isExtern) { + symbol_iterator RelocSym = Reloc->getSymbol(); + Symbol = *RelocSym; } } - if (reloc_found && isExtern) { + if (r_type == MachO::ARM_RELOC_HALF || + r_type == MachO::ARM_RELOC_SECTDIFF || + r_type == MachO::ARM_RELOC_LOCAL_SECTDIFF || + r_type == MachO::ARM_RELOC_HALF_SECTDIFF) { + DataRefImpl RelNext = Rel; + info->O->moveRelocationNext(RelNext); + MachO::any_relocation_info RENext; + RENext = info->O->getRelocation(RelNext); + other_half = info->O->getAnyRelocationAddress(RENext) & 0xffff; + if (info->O->isRelocationScattered(RENext)) + pair_r_value = info->O->getScatteredRelocationValue(RENext); + } + + if (isExtern) { StringRef SymName; Symbol.getName(SymName); const char *name = SymName.data(); op_info->AddSymbol.Present = 1; op_info->AddSymbol.Name = name; - if (value != 0) { - switch (r_type) { - case MachO::ARM_RELOC_HALF: - if ((r_length & 0x1) == 1) { - op_info->Value = value << 16 | other_half; - op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_HI16; - } else { - op_info->Value = other_half << 16 | value; - op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_LO16; - } - break; - default: - break; - } - } else { - switch (r_type) { - case MachO::ARM_RELOC_HALF: - if ((r_length & 0x1) == 1) { - op_info->Value = value << 16 | other_half; - op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_HI16; - } else { - op_info->Value = other_half << 16 | value; - op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_LO16; - } - break; - default: - break; + switch (r_type) { + case MachO::ARM_RELOC_HALF: + if ((r_length & 0x1) == 1) { + op_info->Value = value << 16 | other_half; + op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_HI16; + } else { + op_info->Value = other_half << 16 | value; + op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_LO16; } + break; + default: + break; } return 1; } @@ -994,33 +1997,31 @@ int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset, // return 0 so the code in tryAddingSymbolicOperand() can use the // SymbolLookUp call back with the branch target address to look up the // symbol and possiblity add an annotation for a symbol stub. - if (reloc_found && isExtern == 0 && (r_type == MachO::ARM_RELOC_BR24 || - r_type == MachO::ARM_THUMB_RELOC_BR22)) + if (isExtern == 0 && (r_type == MachO::ARM_RELOC_BR24 || + r_type == MachO::ARM_THUMB_RELOC_BR22)) return 0; uint32_t offset = 0; - if (reloc_found) { - if (r_type == MachO::ARM_RELOC_HALF || - r_type == MachO::ARM_RELOC_HALF_SECTDIFF) { - if ((r_length & 0x1) == 1) - value = value << 16 | other_half; - else - value = other_half << 16 | value; - } - if (r_scattered && (r_type != MachO::ARM_RELOC_HALF && - r_type != MachO::ARM_RELOC_HALF_SECTDIFF)) { - offset = value - r_value; - value = r_value; - } + if (r_type == MachO::ARM_RELOC_HALF || + r_type == MachO::ARM_RELOC_HALF_SECTDIFF) { + if ((r_length & 0x1) == 1) + value = value << 16 | other_half; + else + value = other_half << 16 | value; + } + if (r_scattered && (r_type != MachO::ARM_RELOC_HALF && + r_type != MachO::ARM_RELOC_HALF_SECTDIFF)) { + offset = value - r_value; + value = r_value; } - if (reloc_found && r_type == MachO::ARM_RELOC_HALF_SECTDIFF) { + if (r_type == MachO::ARM_RELOC_HALF_SECTDIFF) { if ((r_length & 0x1) == 1) op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_HI16; else op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_LO16; - const char *add = GuessSymbolName(r_value, info); - const char *sub = GuessSymbolName(pair_r_value, info); + const char *add = GuessSymbolName(r_value, info->AddrMap); + const char *sub = GuessSymbolName(pair_r_value, info->AddrMap); int32_t offset = value - (r_value - pair_r_value); op_info->AddSymbol.Present = 1; if (add != nullptr) @@ -1036,115 +2037,101 @@ int SymbolizerGetOpInfo(void *DisInfo, uint64_t Pc, uint64_t Offset, return 1; } - if (reloc_found == false) - return 0; - op_info->AddSymbol.Present = 1; op_info->Value = offset; - if (reloc_found) { - if (r_type == MachO::ARM_RELOC_HALF) { - if ((r_length & 0x1) == 1) - op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_HI16; - else - op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_LO16; - } + if (r_type == MachO::ARM_RELOC_HALF) { + if ((r_length & 0x1) == 1) + op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_HI16; + else + op_info->VariantKind = LLVMDisassembler_VariantKind_ARM_LO16; } - const char *add = GuessSymbolName(value, info); + const char *add = GuessSymbolName(value, info->AddrMap); if (add != nullptr) { op_info->AddSymbol.Name = add; return 1; } op_info->AddSymbol.Value = value; return 1; - } else if (Arch == Triple::aarch64) { + } + if (Arch == Triple::aarch64) { if (Offset != 0 || Size != 4) return 0; // First search the section's relocation entries (if any) for an entry // for this section offset. uint64_t sect_addr = info->S.getAddress(); uint64_t sect_offset = (Pc + Offset) - sect_addr; - bool reloc_found = false; - DataRefImpl Rel; - MachO::any_relocation_info RE; - bool isExtern = false; - SymbolRef Symbol; - uint32_t r_type = 0; - for (const RelocationRef &Reloc : info->S.relocations()) { - uint64_t RelocOffset; - Reloc.getOffset(RelocOffset); - if (RelocOffset == sect_offset) { - Rel = Reloc.getRawDataRefImpl(); - RE = info->O->getRelocation(Rel); - r_type = info->O->getAnyRelocationType(RE); - if (r_type == MachO::ARM64_RELOC_ADDEND) { - DataRefImpl RelNext = Rel; - info->O->moveRelocationNext(RelNext); - MachO::any_relocation_info RENext = info->O->getRelocation(RelNext); - if (value == 0) { - value = info->O->getPlainRelocationSymbolNum(RENext); - op_info->Value = value; - } - } - // NOTE: Scattered relocations don't exist on arm64. - isExtern = info->O->getPlainRelocationExternal(RE); - if (isExtern) { - symbol_iterator RelocSym = Reloc.getSymbol(); - Symbol = *RelocSym; - } - reloc_found = true; - break; + auto Reloc = + std::find_if(info->S.relocations().begin(), info->S.relocations().end(), + [&](const RelocationRef &Reloc) { + uint64_t RelocOffset; + Reloc.getOffset(RelocOffset); + return RelocOffset == sect_offset; + }); + + if (Reloc == info->S.relocations().end()) + return 0; + + DataRefImpl Rel = Reloc->getRawDataRefImpl(); + MachO::any_relocation_info RE = info->O->getRelocation(Rel); + uint32_t r_type = info->O->getAnyRelocationType(RE); + if (r_type == MachO::ARM64_RELOC_ADDEND) { + DataRefImpl RelNext = Rel; + info->O->moveRelocationNext(RelNext); + MachO::any_relocation_info RENext = info->O->getRelocation(RelNext); + if (value == 0) { + value = info->O->getPlainRelocationSymbolNum(RENext); + op_info->Value = value; } } - if (reloc_found && isExtern) { - StringRef SymName; - Symbol.getName(SymName); - const char *name = SymName.data(); - op_info->AddSymbol.Present = 1; - op_info->AddSymbol.Name = name; + // NOTE: Scattered relocations don't exist on arm64. + if (!info->O->getPlainRelocationExternal(RE)) + return 0; + StringRef SymName; + Reloc->getSymbol()->getName(SymName); + const char *name = SymName.data(); + op_info->AddSymbol.Present = 1; + op_info->AddSymbol.Name = name; - switch (r_type) { - case MachO::ARM64_RELOC_PAGE21: - /* @page */ - op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_PAGE; - break; - case MachO::ARM64_RELOC_PAGEOFF12: - /* @pageoff */ - op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_PAGEOFF; - break; - case MachO::ARM64_RELOC_GOT_LOAD_PAGE21: - /* @gotpage */ - op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_GOTPAGE; - break; - case MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12: - /* @gotpageoff */ - op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_GOTPAGEOFF; - break; - case MachO::ARM64_RELOC_TLVP_LOAD_PAGE21: - /* @tvlppage is not implemented in llvm-mc */ - op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_TLVP; - break; - case MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12: - /* @tvlppageoff is not implemented in llvm-mc */ - op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_TLVOFF; - break; - default: - case MachO::ARM64_RELOC_BRANCH26: - op_info->VariantKind = LLVMDisassembler_VariantKind_None; - break; - } - return 1; + switch (r_type) { + case MachO::ARM64_RELOC_PAGE21: + /* @page */ + op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_PAGE; + break; + case MachO::ARM64_RELOC_PAGEOFF12: + /* @pageoff */ + op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_PAGEOFF; + break; + case MachO::ARM64_RELOC_GOT_LOAD_PAGE21: + /* @gotpage */ + op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_GOTPAGE; + break; + case MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12: + /* @gotpageoff */ + op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_GOTPAGEOFF; + break; + case MachO::ARM64_RELOC_TLVP_LOAD_PAGE21: + /* @tvlppage is not implemented in llvm-mc */ + op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_TLVP; + break; + case MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12: + /* @tvlppageoff is not implemented in llvm-mc */ + op_info->VariantKind = LLVMDisassembler_VariantKind_ARM64_TLVOFF; + break; + default: + case MachO::ARM64_RELOC_BRANCH26: + op_info->VariantKind = LLVMDisassembler_VariantKind_None; + break; } - return 0; - } else { - return 0; + return 1; } + return 0; } // GuessCstringPointer is passed the address of what might be a pointer to a // literal string in a cstring section. If that address is in a cstring section // it returns a pointer to that string. Else it returns nullptr. -const char *GuessCstringPointer(uint64_t ReferenceValue, - struct DisassembleInfo *info) { +static const char *GuessCstringPointer(uint64_t ReferenceValue, + struct DisassembleInfo *info) { uint32_t LoadCommandCount = info->O->getHeader().ncmds; MachOObjectFile::LoadCommandInfo Load = info->O->getFirstLoadCommandInfo(); for (unsigned I = 0;; ++I) { @@ -1429,14 +2416,24 @@ static uint64_t GuessPointerPointer(uint64_t ReferenceValue, // offset into the section, number of bytes left in the section past the offset // and which section is was being referenced. If the Address is not in a // section nullptr is returned. -const char *get_pointer_64(uint64_t Address, uint32_t &offset, uint32_t &left, - SectionRef &S, DisassembleInfo *info) { +static const char *get_pointer_64(uint64_t Address, uint32_t &offset, + uint32_t &left, SectionRef &S, + DisassembleInfo *info, + bool objc_only = false) { offset = 0; left = 0; S = SectionRef(); for (unsigned SectIdx = 0; SectIdx != info->Sections->size(); SectIdx++) { uint64_t SectAddress = ((*(info->Sections))[SectIdx]).getAddress(); uint64_t SectSize = ((*(info->Sections))[SectIdx]).getSize(); + if (objc_only) { + StringRef SectName; + ((*(info->Sections))[SectIdx]).getName(SectName); + DataRefImpl Ref = ((*(info->Sections))[SectIdx]).getRawDataRefImpl(); + StringRef SegName = info->O->getSectionFinalSegmentName(Ref); + if (SegName != "__OBJC" && SectName != "__cstring") + continue; + } if (Address >= SectAddress && Address < SectAddress + SectSize) { S = (*(info->Sections))[SectIdx]; offset = Address - SectAddress; @@ -1449,13 +2446,24 @@ const char *get_pointer_64(uint64_t Address, uint32_t &offset, uint32_t &left, return nullptr; } +static const char *get_pointer_32(uint32_t Address, uint32_t &offset, + uint32_t &left, SectionRef &S, + DisassembleInfo *info, + bool objc_only = false) { + return get_pointer_64(Address, offset, left, S, info, objc_only); +} + // get_symbol_64() returns the name of a symbol (or nullptr) and the address of // the symbol indirectly through n_value. Based on the relocation information // for the specified section offset in the specified section reference. -const char *get_symbol_64(uint32_t sect_offset, SectionRef S, - DisassembleInfo *info, uint64_t &n_value) { +// If no relocation information is found and a non-zero ReferenceValue for the +// symbol is passed, look up that address in the info's AddrMap. +static const char * +get_symbol_64(uint32_t sect_offset, SectionRef S, DisassembleInfo *info, + uint64_t &n_value, + uint64_t ReferenceValue = UnknownAddressOrSize) { n_value = 0; - if (info->verbose == false) + if (!info->verbose) return nullptr; // See if there is an external relocation entry at the sect_offset. @@ -1487,6 +2495,8 @@ const char *get_symbol_64(uint32_t sect_offset, SectionRef S, const char *SymbolName = nullptr; if (reloc_found && isExtern) { Symbol.getAddress(n_value); + if (n_value == UnknownAddressOrSize) + n_value = 0; StringRef name; Symbol.getName(name); if (!name.empty()) { @@ -1504,17 +2514,21 @@ const char *get_symbol_64(uint32_t sect_offset, SectionRef S, // // NOTE: need add passing the database_offset to this routine. - // TODO: We did not find an external relocation entry so look up the - // ReferenceValue as an address of a symbol and if found return that symbol's - // name. - // - // NOTE: need add passing the ReferenceValue to this routine. Then that code - // would simply be this: - // SymbolName = GuessSymbolName(ReferenceValue, info); + // We did not find an external relocation entry so look up the ReferenceValue + // as an address of a symbol and if found return that symbol's name. + if (ReferenceValue != UnknownAddressOrSize) + SymbolName = GuessSymbolName(ReferenceValue, info->AddrMap); return SymbolName; } +static const char *get_symbol_32(uint32_t sect_offset, SectionRef S, + DisassembleInfo *info, + uint32_t ReferenceValue) { + uint64_t n_value64; + return get_symbol_64(sect_offset, S, info, n_value64, ReferenceValue); +} + // These are structs in the Objective-C meta data and read to produce the // comments for disassembly. While these are part of the ABI they are no // public defintions. So the are here not in include/llvm/Support/MachO.h . @@ -1536,6 +2550,14 @@ struct class64_t { uint64_t data; // class_ro64_t * (64-bit pointer) }; +struct class32_t { + uint32_t isa; /* class32_t * (32-bit pointer) */ + uint32_t superclass; /* class32_t * (32-bit pointer) */ + uint32_t cache; /* Cache (32-bit pointer) */ + uint32_t vtable; /* IMP * (32-bit pointer) */ + uint32_t data; /* class_ro32_t * (32-bit pointer) */ +}; + struct class_ro64_t { uint32_t flags; uint32_t instanceStart; @@ -1550,6 +2572,276 @@ struct class_ro64_t { uint64_t baseProperties; // const struct objc_property_list (64-bit pointer) }; +struct class_ro32_t { + uint32_t flags; + uint32_t instanceStart; + uint32_t instanceSize; + uint32_t ivarLayout; /* const uint8_t * (32-bit pointer) */ + uint32_t name; /* const char * (32-bit pointer) */ + uint32_t baseMethods; /* const method_list_t * (32-bit pointer) */ + uint32_t baseProtocols; /* const protocol_list_t * (32-bit pointer) */ + uint32_t ivars; /* const ivar_list_t * (32-bit pointer) */ + uint32_t weakIvarLayout; /* const uint8_t * (32-bit pointer) */ + uint32_t baseProperties; /* const struct objc_property_list * + (32-bit pointer) */ +}; + +/* Values for class_ro{64,32}_t->flags */ +#define RO_META (1 << 0) +#define RO_ROOT (1 << 1) +#define RO_HAS_CXX_STRUCTORS (1 << 2) + +struct method_list64_t { + uint32_t entsize; + uint32_t count; + /* struct method64_t first; These structures follow inline */ +}; + +struct method_list32_t { + uint32_t entsize; + uint32_t count; + /* struct method32_t first; These structures follow inline */ +}; + +struct method64_t { + uint64_t name; /* SEL (64-bit pointer) */ + uint64_t types; /* const char * (64-bit pointer) */ + uint64_t imp; /* IMP (64-bit pointer) */ +}; + +struct method32_t { + uint32_t name; /* SEL (32-bit pointer) */ + uint32_t types; /* const char * (32-bit pointer) */ + uint32_t imp; /* IMP (32-bit pointer) */ +}; + +struct protocol_list64_t { + uint64_t count; /* uintptr_t (a 64-bit value) */ + /* struct protocol64_t * list[0]; These pointers follow inline */ +}; + +struct protocol_list32_t { + uint32_t count; /* uintptr_t (a 32-bit value) */ + /* struct protocol32_t * list[0]; These pointers follow inline */ +}; + +struct protocol64_t { + uint64_t isa; /* id * (64-bit pointer) */ + uint64_t name; /* const char * (64-bit pointer) */ + uint64_t protocols; /* struct protocol_list64_t * + (64-bit pointer) */ + uint64_t instanceMethods; /* method_list_t * (64-bit pointer) */ + uint64_t classMethods; /* method_list_t * (64-bit pointer) */ + uint64_t optionalInstanceMethods; /* method_list_t * (64-bit pointer) */ + uint64_t optionalClassMethods; /* method_list_t * (64-bit pointer) */ + uint64_t instanceProperties; /* struct objc_property_list * + (64-bit pointer) */ +}; + +struct protocol32_t { + uint32_t isa; /* id * (32-bit pointer) */ + uint32_t name; /* const char * (32-bit pointer) */ + uint32_t protocols; /* struct protocol_list_t * + (32-bit pointer) */ + uint32_t instanceMethods; /* method_list_t * (32-bit pointer) */ + uint32_t classMethods; /* method_list_t * (32-bit pointer) */ + uint32_t optionalInstanceMethods; /* method_list_t * (32-bit pointer) */ + uint32_t optionalClassMethods; /* method_list_t * (32-bit pointer) */ + uint32_t instanceProperties; /* struct objc_property_list * + (32-bit pointer) */ +}; + +struct ivar_list64_t { + uint32_t entsize; + uint32_t count; + /* struct ivar64_t first; These structures follow inline */ +}; + +struct ivar_list32_t { + uint32_t entsize; + uint32_t count; + /* struct ivar32_t first; These structures follow inline */ +}; + +struct ivar64_t { + uint64_t offset; /* uintptr_t * (64-bit pointer) */ + uint64_t name; /* const char * (64-bit pointer) */ + uint64_t type; /* const char * (64-bit pointer) */ + uint32_t alignment; + uint32_t size; +}; + +struct ivar32_t { + uint32_t offset; /* uintptr_t * (32-bit pointer) */ + uint32_t name; /* const char * (32-bit pointer) */ + uint32_t type; /* const char * (32-bit pointer) */ + uint32_t alignment; + uint32_t size; +}; + +struct objc_property_list64 { + uint32_t entsize; + uint32_t count; + /* struct objc_property64 first; These structures follow inline */ +}; + +struct objc_property_list32 { + uint32_t entsize; + uint32_t count; + /* struct objc_property32 first; These structures follow inline */ +}; + +struct objc_property64 { + uint64_t name; /* const char * (64-bit pointer) */ + uint64_t attributes; /* const char * (64-bit pointer) */ +}; + +struct objc_property32 { + uint32_t name; /* const char * (32-bit pointer) */ + uint32_t attributes; /* const char * (32-bit pointer) */ +}; + +struct category64_t { + uint64_t name; /* const char * (64-bit pointer) */ + uint64_t cls; /* struct class_t * (64-bit pointer) */ + uint64_t instanceMethods; /* struct method_list_t * (64-bit pointer) */ + uint64_t classMethods; /* struct method_list_t * (64-bit pointer) */ + uint64_t protocols; /* struct protocol_list_t * (64-bit pointer) */ + uint64_t instanceProperties; /* struct objc_property_list * + (64-bit pointer) */ +}; + +struct category32_t { + uint32_t name; /* const char * (32-bit pointer) */ + uint32_t cls; /* struct class_t * (32-bit pointer) */ + uint32_t instanceMethods; /* struct method_list_t * (32-bit pointer) */ + uint32_t classMethods; /* struct method_list_t * (32-bit pointer) */ + uint32_t protocols; /* struct protocol_list_t * (32-bit pointer) */ + uint32_t instanceProperties; /* struct objc_property_list * + (32-bit pointer) */ +}; + +struct objc_image_info64 { + uint32_t version; + uint32_t flags; +}; +struct objc_image_info32 { + uint32_t version; + uint32_t flags; +}; +struct imageInfo_t { + uint32_t version; + uint32_t flags; +}; +/* masks for objc_image_info.flags */ +#define OBJC_IMAGE_IS_REPLACEMENT (1 << 0) +#define OBJC_IMAGE_SUPPORTS_GC (1 << 1) + +struct message_ref64 { + uint64_t imp; /* IMP (64-bit pointer) */ + uint64_t sel; /* SEL (64-bit pointer) */ +}; + +struct message_ref32 { + uint32_t imp; /* IMP (32-bit pointer) */ + uint32_t sel; /* SEL (32-bit pointer) */ +}; + +// Objective-C 1 (32-bit only) meta data structs. + +struct objc_module_t { + uint32_t version; + uint32_t size; + uint32_t name; /* char * (32-bit pointer) */ + uint32_t symtab; /* struct objc_symtab * (32-bit pointer) */ +}; + +struct objc_symtab_t { + uint32_t sel_ref_cnt; + uint32_t refs; /* SEL * (32-bit pointer) */ + uint16_t cls_def_cnt; + uint16_t cat_def_cnt; + // uint32_t defs[1]; /* void * (32-bit pointer) variable size */ +}; + +struct objc_class_t { + uint32_t isa; /* struct objc_class * (32-bit pointer) */ + uint32_t super_class; /* struct objc_class * (32-bit pointer) */ + uint32_t name; /* const char * (32-bit pointer) */ + int32_t version; + int32_t info; + int32_t instance_size; + uint32_t ivars; /* struct objc_ivar_list * (32-bit pointer) */ + uint32_t methodLists; /* struct objc_method_list ** (32-bit pointer) */ + uint32_t cache; /* struct objc_cache * (32-bit pointer) */ + uint32_t protocols; /* struct objc_protocol_list * (32-bit pointer) */ +}; + +#define CLS_GETINFO(cls, infomask) ((cls)->info & (infomask)) +// class is not a metaclass +#define CLS_CLASS 0x1 +// class is a metaclass +#define CLS_META 0x2 + +struct objc_category_t { + uint32_t category_name; /* char * (32-bit pointer) */ + uint32_t class_name; /* char * (32-bit pointer) */ + uint32_t instance_methods; /* struct objc_method_list * (32-bit pointer) */ + uint32_t class_methods; /* struct objc_method_list * (32-bit pointer) */ + uint32_t protocols; /* struct objc_protocol_list * (32-bit ptr) */ +}; + +struct objc_ivar_t { + uint32_t ivar_name; /* char * (32-bit pointer) */ + uint32_t ivar_type; /* char * (32-bit pointer) */ + int32_t ivar_offset; +}; + +struct objc_ivar_list_t { + int32_t ivar_count; + // struct objc_ivar_t ivar_list[1]; /* variable length structure */ +}; + +struct objc_method_list_t { + uint32_t obsolete; /* struct objc_method_list * (32-bit pointer) */ + int32_t method_count; + // struct objc_method_t method_list[1]; /* variable length structure */ +}; + +struct objc_method_t { + uint32_t method_name; /* SEL, aka struct objc_selector * (32-bit pointer) */ + uint32_t method_types; /* char * (32-bit pointer) */ + uint32_t method_imp; /* IMP, aka function pointer, (*IMP)(id, SEL, ...) + (32-bit pointer) */ +}; + +struct objc_protocol_list_t { + uint32_t next; /* struct objc_protocol_list * (32-bit pointer) */ + int32_t count; + // uint32_t list[1]; /* Protocol *, aka struct objc_protocol_t * + // (32-bit pointer) */ +}; + +struct objc_protocol_t { + uint32_t isa; /* struct objc_class * (32-bit pointer) */ + uint32_t protocol_name; /* char * (32-bit pointer) */ + uint32_t protocol_list; /* struct objc_protocol_list * (32-bit pointer) */ + uint32_t instance_methods; /* struct objc_method_description_list * + (32-bit pointer) */ + uint32_t class_methods; /* struct objc_method_description_list * + (32-bit pointer) */ +}; + +struct objc_method_description_list_t { + int32_t count; + // struct objc_method_description_t list[1]; +}; + +struct objc_method_description_t { + uint32_t name; /* SEL, aka struct objc_selector * (32-bit pointer) */ + uint32_t types; /* char * (32-bit pointer) */ +}; + inline void swapStruct(struct cfstring64_t &cfs) { sys::swapByteOrder(cfs.isa); sys::swapByteOrder(cfs.flags); @@ -1565,6 +2857,14 @@ inline void swapStruct(struct class64_t &c) { sys::swapByteOrder(c.data); } +inline void swapStruct(struct class32_t &c) { + sys::swapByteOrder(c.isa); + sys::swapByteOrder(c.superclass); + sys::swapByteOrder(c.cache); + sys::swapByteOrder(c.vtable); + sys::swapByteOrder(c.data); +} + inline void swapStruct(struct class_ro64_t &cro) { sys::swapByteOrder(cro.flags); sys::swapByteOrder(cro.instanceStart); @@ -1579,6 +2879,238 @@ inline void swapStruct(struct class_ro64_t &cro) { sys::swapByteOrder(cro.baseProperties); } +inline void swapStruct(struct class_ro32_t &cro) { + sys::swapByteOrder(cro.flags); + sys::swapByteOrder(cro.instanceStart); + sys::swapByteOrder(cro.instanceSize); + sys::swapByteOrder(cro.ivarLayout); + sys::swapByteOrder(cro.name); + sys::swapByteOrder(cro.baseMethods); + sys::swapByteOrder(cro.baseProtocols); + sys::swapByteOrder(cro.ivars); + sys::swapByteOrder(cro.weakIvarLayout); + sys::swapByteOrder(cro.baseProperties); +} + +inline void swapStruct(struct method_list64_t &ml) { + sys::swapByteOrder(ml.entsize); + sys::swapByteOrder(ml.count); +} + +inline void swapStruct(struct method_list32_t &ml) { + sys::swapByteOrder(ml.entsize); + sys::swapByteOrder(ml.count); +} + +inline void swapStruct(struct method64_t &m) { + sys::swapByteOrder(m.name); + sys::swapByteOrder(m.types); + sys::swapByteOrder(m.imp); +} + +inline void swapStruct(struct method32_t &m) { + sys::swapByteOrder(m.name); + sys::swapByteOrder(m.types); + sys::swapByteOrder(m.imp); +} + +inline void swapStruct(struct protocol_list64_t &pl) { + sys::swapByteOrder(pl.count); +} + +inline void swapStruct(struct protocol_list32_t &pl) { + sys::swapByteOrder(pl.count); +} + +inline void swapStruct(struct protocol64_t &p) { + sys::swapByteOrder(p.isa); + sys::swapByteOrder(p.name); + sys::swapByteOrder(p.protocols); + sys::swapByteOrder(p.instanceMethods); + sys::swapByteOrder(p.classMethods); + sys::swapByteOrder(p.optionalInstanceMethods); + sys::swapByteOrder(p.optionalClassMethods); + sys::swapByteOrder(p.instanceProperties); +} + +inline void swapStruct(struct protocol32_t &p) { + sys::swapByteOrder(p.isa); + sys::swapByteOrder(p.name); + sys::swapByteOrder(p.protocols); + sys::swapByteOrder(p.instanceMethods); + sys::swapByteOrder(p.classMethods); + sys::swapByteOrder(p.optionalInstanceMethods); + sys::swapByteOrder(p.optionalClassMethods); + sys::swapByteOrder(p.instanceProperties); +} + +inline void swapStruct(struct ivar_list64_t &il) { + sys::swapByteOrder(il.entsize); + sys::swapByteOrder(il.count); +} + +inline void swapStruct(struct ivar_list32_t &il) { + sys::swapByteOrder(il.entsize); + sys::swapByteOrder(il.count); +} + +inline void swapStruct(struct ivar64_t &i) { + sys::swapByteOrder(i.offset); + sys::swapByteOrder(i.name); + sys::swapByteOrder(i.type); + sys::swapByteOrder(i.alignment); + sys::swapByteOrder(i.size); +} + +inline void swapStruct(struct ivar32_t &i) { + sys::swapByteOrder(i.offset); + sys::swapByteOrder(i.name); + sys::swapByteOrder(i.type); + sys::swapByteOrder(i.alignment); + sys::swapByteOrder(i.size); +} + +inline void swapStruct(struct objc_property_list64 &pl) { + sys::swapByteOrder(pl.entsize); + sys::swapByteOrder(pl.count); +} + +inline void swapStruct(struct objc_property_list32 &pl) { + sys::swapByteOrder(pl.entsize); + sys::swapByteOrder(pl.count); +} + +inline void swapStruct(struct objc_property64 &op) { + sys::swapByteOrder(op.name); + sys::swapByteOrder(op.attributes); +} + +inline void swapStruct(struct objc_property32 &op) { + sys::swapByteOrder(op.name); + sys::swapByteOrder(op.attributes); +} + +inline void swapStruct(struct category64_t &c) { + sys::swapByteOrder(c.name); + sys::swapByteOrder(c.cls); + sys::swapByteOrder(c.instanceMethods); + sys::swapByteOrder(c.classMethods); + sys::swapByteOrder(c.protocols); + sys::swapByteOrder(c.instanceProperties); +} + +inline void swapStruct(struct category32_t &c) { + sys::swapByteOrder(c.name); + sys::swapByteOrder(c.cls); + sys::swapByteOrder(c.instanceMethods); + sys::swapByteOrder(c.classMethods); + sys::swapByteOrder(c.protocols); + sys::swapByteOrder(c.instanceProperties); +} + +inline void swapStruct(struct objc_image_info64 &o) { + sys::swapByteOrder(o.version); + sys::swapByteOrder(o.flags); +} + +inline void swapStruct(struct objc_image_info32 &o) { + sys::swapByteOrder(o.version); + sys::swapByteOrder(o.flags); +} + +inline void swapStruct(struct imageInfo_t &o) { + sys::swapByteOrder(o.version); + sys::swapByteOrder(o.flags); +} + +inline void swapStruct(struct message_ref64 &mr) { + sys::swapByteOrder(mr.imp); + sys::swapByteOrder(mr.sel); +} + +inline void swapStruct(struct message_ref32 &mr) { + sys::swapByteOrder(mr.imp); + sys::swapByteOrder(mr.sel); +} + +inline void swapStruct(struct objc_module_t &module) { + sys::swapByteOrder(module.version); + sys::swapByteOrder(module.size); + sys::swapByteOrder(module.name); + sys::swapByteOrder(module.symtab); +} + +inline void swapStruct(struct objc_symtab_t &symtab) { + sys::swapByteOrder(symtab.sel_ref_cnt); + sys::swapByteOrder(symtab.refs); + sys::swapByteOrder(symtab.cls_def_cnt); + sys::swapByteOrder(symtab.cat_def_cnt); +} + +inline void swapStruct(struct objc_class_t &objc_class) { + sys::swapByteOrder(objc_class.isa); + sys::swapByteOrder(objc_class.super_class); + sys::swapByteOrder(objc_class.name); + sys::swapByteOrder(objc_class.version); + sys::swapByteOrder(objc_class.info); + sys::swapByteOrder(objc_class.instance_size); + sys::swapByteOrder(objc_class.ivars); + sys::swapByteOrder(objc_class.methodLists); + sys::swapByteOrder(objc_class.cache); + sys::swapByteOrder(objc_class.protocols); +} + +inline void swapStruct(struct objc_category_t &objc_category) { + sys::swapByteOrder(objc_category.category_name); + sys::swapByteOrder(objc_category.class_name); + sys::swapByteOrder(objc_category.instance_methods); + sys::swapByteOrder(objc_category.class_methods); + sys::swapByteOrder(objc_category.protocols); +} + +inline void swapStruct(struct objc_ivar_list_t &objc_ivar_list) { + sys::swapByteOrder(objc_ivar_list.ivar_count); +} + +inline void swapStruct(struct objc_ivar_t &objc_ivar) { + sys::swapByteOrder(objc_ivar.ivar_name); + sys::swapByteOrder(objc_ivar.ivar_type); + sys::swapByteOrder(objc_ivar.ivar_offset); +} + +inline void swapStruct(struct objc_method_list_t &method_list) { + sys::swapByteOrder(method_list.obsolete); + sys::swapByteOrder(method_list.method_count); +} + +inline void swapStruct(struct objc_method_t &method) { + sys::swapByteOrder(method.method_name); + sys::swapByteOrder(method.method_types); + sys::swapByteOrder(method.method_imp); +} + +inline void swapStruct(struct objc_protocol_list_t &protocol_list) { + sys::swapByteOrder(protocol_list.next); + sys::swapByteOrder(protocol_list.count); +} + +inline void swapStruct(struct objc_protocol_t &protocol) { + sys::swapByteOrder(protocol.isa); + sys::swapByteOrder(protocol.protocol_name); + sys::swapByteOrder(protocol.protocol_list); + sys::swapByteOrder(protocol.instance_methods); + sys::swapByteOrder(protocol.class_methods); +} + +inline void swapStruct(struct objc_method_description_list_t &mdl) { + sys::swapByteOrder(mdl.count); +} + +inline void swapStruct(struct objc_method_description_t &md) { + sys::swapByteOrder(md.name); + sys::swapByteOrder(md.types); +} + static const char *get_dyld_bind_info_symbolname(uint64_t ReferenceValue, struct DisassembleInfo *info); @@ -1587,9 +3119,9 @@ static const char *get_dyld_bind_info_symbolname(uint64_t ReferenceValue, // address of the pointer, so when the pointer is zero as it can be in an .o // file, that is used to look for an external relocation entry with a symbol // name. -const char *get_objc2_64bit_class_name(uint64_t pointer_value, - uint64_t ReferenceValue, - struct DisassembleInfo *info) { +static const char *get_objc2_64bit_class_name(uint64_t pointer_value, + uint64_t ReferenceValue, + struct DisassembleInfo *info) { const char *r; uint32_t offset, left; SectionRef S; @@ -1638,8 +3170,8 @@ const char *get_objc2_64bit_class_name(uint64_t pointer_value, // get_objc2_64bit_cfstring_name is used for disassembly and is passed a // pointer to a cfstring and returns its name or nullptr. -const char *get_objc2_64bit_cfstring_name(uint64_t ReferenceValue, - struct DisassembleInfo *info) { +static const char *get_objc2_64bit_cfstring_name(uint64_t ReferenceValue, + struct DisassembleInfo *info) { const char *r, *name; uint32_t offset, left; SectionRef S; @@ -1672,8 +3204,8 @@ const char *get_objc2_64bit_cfstring_name(uint64_t ReferenceValue, // who's symbol's n_value is the real pointer to the selector name. If that is // the case the real pointer to the selector name is returned else 0 is // returned -uint64_t get_objc2_64bit_selref(uint64_t ReferenceValue, - struct DisassembleInfo *info) { +static uint64_t get_objc2_64bit_selref(uint64_t ReferenceValue, + struct DisassembleInfo *info) { uint32_t offset, left; SectionRef S; @@ -1687,6 +3219,2411 @@ uint64_t get_objc2_64bit_selref(uint64_t ReferenceValue, return n_value; } +static const SectionRef get_section(MachOObjectFile *O, const char *segname, + const char *sectname) { + for (const SectionRef &Section : O->sections()) { + StringRef SectName; + Section.getName(SectName); + DataRefImpl Ref = Section.getRawDataRefImpl(); + StringRef SegName = O->getSectionFinalSegmentName(Ref); + if (SegName == segname && SectName == sectname) + return Section; + } + return SectionRef(); +} + +static void +walk_pointer_list_64(const char *listname, const SectionRef S, + MachOObjectFile *O, struct DisassembleInfo *info, + void (*func)(uint64_t, struct DisassembleInfo *info)) { + if (S == SectionRef()) + return; + + StringRef SectName; + S.getName(SectName); + DataRefImpl Ref = S.getRawDataRefImpl(); + StringRef SegName = O->getSectionFinalSegmentName(Ref); + outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; + + StringRef BytesStr; + S.getContents(BytesStr); + const char *Contents = reinterpret_cast<const char *>(BytesStr.data()); + + for (uint32_t i = 0; i < S.getSize(); i += sizeof(uint64_t)) { + uint32_t left = S.getSize() - i; + uint32_t size = left < sizeof(uint64_t) ? left : sizeof(uint64_t); + uint64_t p = 0; + memcpy(&p, Contents + i, size); + if (i + sizeof(uint64_t) > S.getSize()) + outs() << listname << " list pointer extends past end of (" << SegName + << "," << SectName << ") section\n"; + outs() << format("%016" PRIx64, S.getAddress() + i) << " "; + + if (O->isLittleEndian() != sys::IsLittleEndianHost) + sys::swapByteOrder(p); + + uint64_t n_value = 0; + const char *name = get_symbol_64(i, S, info, n_value, p); + if (name == nullptr) + name = get_dyld_bind_info_symbolname(S.getAddress() + i, info); + + if (n_value != 0) { + outs() << format("0x%" PRIx64, n_value); + if (p != 0) + outs() << " + " << format("0x%" PRIx64, p); + } else + outs() << format("0x%" PRIx64, p); + if (name != nullptr) + outs() << " " << name; + outs() << "\n"; + + p += n_value; + if (func) + func(p, info); + } +} + +static void +walk_pointer_list_32(const char *listname, const SectionRef S, + MachOObjectFile *O, struct DisassembleInfo *info, + void (*func)(uint32_t, struct DisassembleInfo *info)) { + if (S == SectionRef()) + return; + + StringRef SectName; + S.getName(SectName); + DataRefImpl Ref = S.getRawDataRefImpl(); + StringRef SegName = O->getSectionFinalSegmentName(Ref); + outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; + + StringRef BytesStr; + S.getContents(BytesStr); + const char *Contents = reinterpret_cast<const char *>(BytesStr.data()); + + for (uint32_t i = 0; i < S.getSize(); i += sizeof(uint32_t)) { + uint32_t left = S.getSize() - i; + uint32_t size = left < sizeof(uint32_t) ? left : sizeof(uint32_t); + uint32_t p = 0; + memcpy(&p, Contents + i, size); + if (i + sizeof(uint32_t) > S.getSize()) + outs() << listname << " list pointer extends past end of (" << SegName + << "," << SectName << ") section\n"; + uint32_t Address = S.getAddress() + i; + outs() << format("%08" PRIx32, Address) << " "; + + if (O->isLittleEndian() != sys::IsLittleEndianHost) + sys::swapByteOrder(p); + outs() << format("0x%" PRIx32, p); + + const char *name = get_symbol_32(i, S, info, p); + if (name != nullptr) + outs() << " " << name; + outs() << "\n"; + + if (func) + func(p, info); + } +} + +static void print_layout_map(const char *layout_map, uint32_t left) { + outs() << " layout map: "; + do { + outs() << format("0x%02" PRIx32, (*layout_map) & 0xff) << " "; + left--; + layout_map++; + } while (*layout_map != '\0' && left != 0); + outs() << "\n"; +} + +static void print_layout_map64(uint64_t p, struct DisassembleInfo *info) { + uint32_t offset, left; + SectionRef S; + const char *layout_map; + + if (p == 0) + return; + layout_map = get_pointer_64(p, offset, left, S, info); + print_layout_map(layout_map, left); +} + +static void print_layout_map32(uint32_t p, struct DisassembleInfo *info) { + uint32_t offset, left; + SectionRef S; + const char *layout_map; + + if (p == 0) + return; + layout_map = get_pointer_32(p, offset, left, S, info); + print_layout_map(layout_map, left); +} + +static void print_method_list64_t(uint64_t p, struct DisassembleInfo *info, + const char *indent) { + struct method_list64_t ml; + struct method64_t m; + const char *r; + uint32_t offset, xoffset, left, i; + SectionRef S, xS; + const char *name, *sym_name; + uint64_t n_value; + + r = get_pointer_64(p, offset, left, S, info); + if (r == nullptr) + return; + memset(&ml, '\0', sizeof(struct method_list64_t)); + if (left < sizeof(struct method_list64_t)) { + memcpy(&ml, r, left); + outs() << " (method_list_t entends past the end of the section)\n"; + } else + memcpy(&ml, r, sizeof(struct method_list64_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(ml); + outs() << indent << "\t\t entsize " << ml.entsize << "\n"; + outs() << indent << "\t\t count " << ml.count << "\n"; + + p += sizeof(struct method_list64_t); + offset += sizeof(struct method_list64_t); + for (i = 0; i < ml.count; i++) { + r = get_pointer_64(p, offset, left, S, info); + if (r == nullptr) + 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"; + } else + memcpy(&m, r, sizeof(struct method64_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(m); + + outs() << indent << "\t\t name "; + sym_name = get_symbol_64(offset + offsetof(struct method64_t, name), S, + info, n_value, m.name); + if (n_value != 0) { + if (info->verbose && sym_name != nullptr) + outs() << sym_name; + else + outs() << format("0x%" PRIx64, n_value); + if (m.name != 0) + outs() << " + " << format("0x%" PRIx64, m.name); + } else + outs() << format("0x%" PRIx64, m.name); + name = get_pointer_64(m.name + n_value, xoffset, left, xS, info); + if (name != nullptr) + outs() << format(" %.*s", left, name); + outs() << "\n"; + + outs() << indent << "\t\t types "; + sym_name = get_symbol_64(offset + offsetof(struct method64_t, types), S, + info, n_value, m.types); + if (n_value != 0) { + if (info->verbose && sym_name != nullptr) + outs() << sym_name; + else + outs() << format("0x%" PRIx64, n_value); + if (m.types != 0) + outs() << " + " << format("0x%" PRIx64, m.types); + } else + outs() << format("0x%" PRIx64, m.types); + name = get_pointer_64(m.types + n_value, xoffset, left, xS, info); + if (name != nullptr) + outs() << format(" %.*s", left, name); + outs() << "\n"; + + outs() << indent << "\t\t imp "; + name = get_symbol_64(offset + offsetof(struct method64_t, imp), S, info, + n_value, m.imp); + if (info->verbose && name == nullptr) { + if (n_value != 0) { + outs() << format("0x%" PRIx64, n_value) << " "; + if (m.imp != 0) + outs() << "+ " << format("0x%" PRIx64, m.imp) << " "; + } else + outs() << format("0x%" PRIx64, m.imp) << " "; + } + if (name != nullptr) + outs() << name; + outs() << "\n"; + + p += sizeof(struct method64_t); + offset += sizeof(struct method64_t); + } +} + +static void print_method_list32_t(uint64_t p, struct DisassembleInfo *info, + const char *indent) { + struct method_list32_t ml; + struct method32_t m; + const char *r, *name; + uint32_t offset, xoffset, left, i; + SectionRef S, xS; + + r = get_pointer_32(p, offset, left, S, info); + if (r == nullptr) + return; + memset(&ml, '\0', sizeof(struct method_list32_t)); + if (left < sizeof(struct method_list32_t)) { + memcpy(&ml, r, left); + outs() << " (method_list_t entends past the end of the section)\n"; + } else + memcpy(&ml, r, sizeof(struct method_list32_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(ml); + outs() << indent << "\t\t entsize " << ml.entsize << "\n"; + outs() << indent << "\t\t count " << ml.count << "\n"; + + p += sizeof(struct method_list32_t); + offset += sizeof(struct method_list32_t); + for (i = 0; i < ml.count; i++) { + r = get_pointer_32(p, offset, left, S, info); + if (r == nullptr) + return; + memset(&m, '\0', sizeof(struct method32_t)); + if (left < sizeof(struct method32_t)) { + memcpy(&ml, r, left); + outs() << indent << " (method_t entends past the end of the section)\n"; + } else + memcpy(&m, r, sizeof(struct method32_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(m); + + outs() << indent << "\t\t name " << format("0x%" PRIx32, m.name); + name = get_pointer_32(m.name, xoffset, left, xS, info); + if (name != nullptr) + outs() << format(" %.*s", left, name); + outs() << "\n"; + + outs() << indent << "\t\t types " << format("0x%" PRIx32, m.types); + name = get_pointer_32(m.types, xoffset, left, xS, info); + if (name != nullptr) + outs() << format(" %.*s", left, name); + outs() << "\n"; + + outs() << indent << "\t\t imp " << format("0x%" PRIx32, m.imp); + name = get_symbol_32(offset + offsetof(struct method32_t, imp), S, info, + m.imp); + if (name != nullptr) + outs() << " " << name; + outs() << "\n"; + + p += sizeof(struct method32_t); + offset += sizeof(struct method32_t); + } +} + +static bool print_method_list(uint32_t p, struct DisassembleInfo *info) { + uint32_t offset, left, xleft; + SectionRef S; + struct objc_method_list_t method_list; + struct objc_method_t method; + const char *r, *methods, *name, *SymbolName; + int32_t i; + + r = get_pointer_32(p, offset, left, S, info, true); + if (r == nullptr) + return true; + + outs() << "\n"; + if (left > sizeof(struct objc_method_list_t)) { + memcpy(&method_list, r, sizeof(struct objc_method_list_t)); + } else { + outs() << "\t\t objc_method_list extends past end of the section\n"; + memset(&method_list, '\0', sizeof(struct objc_method_list_t)); + memcpy(&method_list, r, left); + } + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(method_list); + + outs() << "\t\t obsolete " + << format("0x%08" PRIx32, method_list.obsolete) << "\n"; + outs() << "\t\t method_count " << method_list.method_count << "\n"; + + methods = r + sizeof(struct objc_method_list_t); + for (i = 0; i < method_list.method_count; i++) { + if ((i + 1) * sizeof(struct objc_method_t) > left) { + outs() << "\t\t remaining method's extend past the of the section\n"; + break; + } + memcpy(&method, methods + i * sizeof(struct objc_method_t), + sizeof(struct objc_method_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(method); + + outs() << "\t\t method_name " + << format("0x%08" PRIx32, method.method_name); + if (info->verbose) { + name = get_pointer_32(method.method_name, offset, xleft, S, info, true); + if (name != nullptr) + outs() << format(" %.*s", xleft, name); + else + outs() << " (not in an __OBJC section)"; + } + outs() << "\n"; + + outs() << "\t\t method_types " + << format("0x%08" PRIx32, method.method_types); + if (info->verbose) { + name = get_pointer_32(method.method_types, offset, xleft, S, info, true); + if (name != nullptr) + outs() << format(" %.*s", xleft, name); + else + outs() << " (not in an __OBJC section)"; + } + outs() << "\n"; + + outs() << "\t\t method_imp " + << format("0x%08" PRIx32, method.method_imp) << " "; + if (info->verbose) { + SymbolName = GuessSymbolName(method.method_imp, info->AddrMap); + if (SymbolName != nullptr) + outs() << SymbolName; + } + outs() << "\n"; + } + return false; +} + +static void print_protocol_list64_t(uint64_t p, struct DisassembleInfo *info) { + struct protocol_list64_t pl; + uint64_t q, n_value; + struct protocol64_t pc; + const char *r; + uint32_t offset, xoffset, left, i; + SectionRef S, xS; + const char *name, *sym_name; + + r = get_pointer_64(p, offset, left, S, info); + if (r == nullptr) + return; + memset(&pl, '\0', sizeof(struct protocol_list64_t)); + if (left < sizeof(struct protocol_list64_t)) { + memcpy(&pl, r, left); + outs() << " (protocol_list_t entends past the end of the section)\n"; + } else + memcpy(&pl, r, sizeof(struct protocol_list64_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(pl); + outs() << " count " << pl.count << "\n"; + + p += sizeof(struct protocol_list64_t); + offset += sizeof(struct protocol_list64_t); + for (i = 0; i < pl.count; i++) { + r = get_pointer_64(p, offset, left, S, info); + if (r == nullptr) + return; + q = 0; + if (left < sizeof(uint64_t)) { + memcpy(&q, r, left); + outs() << " (protocol_t * entends past the end of the section)\n"; + } else + memcpy(&q, r, sizeof(uint64_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + sys::swapByteOrder(q); + + outs() << "\t\t list[" << i << "] "; + sym_name = get_symbol_64(offset, S, info, n_value, q); + if (n_value != 0) { + if (info->verbose && sym_name != nullptr) + outs() << sym_name; + else + outs() << format("0x%" PRIx64, n_value); + if (q != 0) + outs() << " + " << format("0x%" PRIx64, q); + } else + outs() << format("0x%" PRIx64, q); + outs() << " (struct protocol_t *)\n"; + + r = get_pointer_64(q + n_value, offset, left, S, info); + if (r == nullptr) + return; + memset(&pc, '\0', sizeof(struct protocol64_t)); + if (left < sizeof(struct protocol64_t)) { + memcpy(&pc, r, left); + outs() << " (protocol_t entends past the end of the section)\n"; + } else + memcpy(&pc, r, sizeof(struct protocol64_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(pc); + + outs() << "\t\t\t isa " << format("0x%" PRIx64, pc.isa) << "\n"; + + outs() << "\t\t\t name "; + sym_name = get_symbol_64(offset + offsetof(struct protocol64_t, name), S, + info, n_value, pc.name); + if (n_value != 0) { + if (info->verbose && sym_name != nullptr) + outs() << sym_name; + else + outs() << format("0x%" PRIx64, n_value); + if (pc.name != 0) + outs() << " + " << format("0x%" PRIx64, pc.name); + } else + outs() << format("0x%" PRIx64, pc.name); + name = get_pointer_64(pc.name + n_value, xoffset, left, xS, info); + if (name != nullptr) + outs() << format(" %.*s", left, name); + outs() << "\n"; + + outs() << "\t\t\tprotocols " << format("0x%" PRIx64, pc.protocols) << "\n"; + + outs() << "\t\t instanceMethods "; + sym_name = + get_symbol_64(offset + offsetof(struct protocol64_t, instanceMethods), + S, info, n_value, pc.instanceMethods); + if (n_value != 0) { + if (info->verbose && sym_name != nullptr) + outs() << sym_name; + else + outs() << format("0x%" PRIx64, n_value); + if (pc.instanceMethods != 0) + outs() << " + " << format("0x%" PRIx64, pc.instanceMethods); + } else + outs() << format("0x%" PRIx64, pc.instanceMethods); + outs() << " (struct method_list_t *)\n"; + if (pc.instanceMethods + n_value != 0) + print_method_list64_t(pc.instanceMethods + n_value, info, "\t"); + + outs() << "\t\t classMethods "; + sym_name = + get_symbol_64(offset + offsetof(struct protocol64_t, classMethods), S, + info, n_value, pc.classMethods); + if (n_value != 0) { + if (info->verbose && sym_name != nullptr) + outs() << sym_name; + else + outs() << format("0x%" PRIx64, n_value); + if (pc.classMethods != 0) + outs() << " + " << format("0x%" PRIx64, pc.classMethods); + } else + outs() << format("0x%" PRIx64, pc.classMethods); + outs() << " (struct method_list_t *)\n"; + if (pc.classMethods + n_value != 0) + print_method_list64_t(pc.classMethods + n_value, info, "\t"); + + outs() << "\t optionalInstanceMethods " + << format("0x%" PRIx64, pc.optionalInstanceMethods) << "\n"; + outs() << "\t optionalClassMethods " + << format("0x%" PRIx64, pc.optionalClassMethods) << "\n"; + outs() << "\t instanceProperties " + << format("0x%" PRIx64, pc.instanceProperties) << "\n"; + + p += sizeof(uint64_t); + offset += sizeof(uint64_t); + } +} + +static void print_protocol_list32_t(uint32_t p, struct DisassembleInfo *info) { + struct protocol_list32_t pl; + uint32_t q; + struct protocol32_t pc; + const char *r; + uint32_t offset, xoffset, left, i; + SectionRef S, xS; + const char *name; + + r = get_pointer_32(p, offset, left, S, info); + if (r == nullptr) + return; + memset(&pl, '\0', sizeof(struct protocol_list32_t)); + if (left < sizeof(struct protocol_list32_t)) { + memcpy(&pl, r, left); + outs() << " (protocol_list_t entends past the end of the section)\n"; + } else + memcpy(&pl, r, sizeof(struct protocol_list32_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(pl); + outs() << " count " << pl.count << "\n"; + + p += sizeof(struct protocol_list32_t); + offset += sizeof(struct protocol_list32_t); + for (i = 0; i < pl.count; i++) { + r = get_pointer_32(p, offset, left, S, info); + if (r == nullptr) + return; + q = 0; + if (left < sizeof(uint32_t)) { + memcpy(&q, r, left); + outs() << " (protocol_t * entends past the end of the section)\n"; + } else + memcpy(&q, r, sizeof(uint32_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + sys::swapByteOrder(q); + outs() << "\t\t list[" << i << "] " << format("0x%" PRIx32, q) + << " (struct protocol_t *)\n"; + r = get_pointer_32(q, offset, left, S, info); + if (r == nullptr) + return; + memset(&pc, '\0', sizeof(struct protocol32_t)); + if (left < sizeof(struct protocol32_t)) { + memcpy(&pc, r, left); + outs() << " (protocol_t entends past the end of the section)\n"; + } else + memcpy(&pc, r, sizeof(struct protocol32_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(pc); + outs() << "\t\t\t isa " << format("0x%" PRIx32, pc.isa) << "\n"; + outs() << "\t\t\t name " << format("0x%" PRIx32, pc.name); + name = get_pointer_32(pc.name, xoffset, left, xS, info); + if (name != nullptr) + outs() << format(" %.*s", left, name); + outs() << "\n"; + outs() << "\t\t\tprotocols " << format("0x%" PRIx32, pc.protocols) << "\n"; + outs() << "\t\t instanceMethods " + << format("0x%" PRIx32, pc.instanceMethods) + << " (struct method_list_t *)\n"; + if (pc.instanceMethods != 0) + print_method_list32_t(pc.instanceMethods, info, "\t"); + outs() << "\t\t classMethods " << format("0x%" PRIx32, pc.classMethods) + << " (struct method_list_t *)\n"; + if (pc.classMethods != 0) + print_method_list32_t(pc.classMethods, info, "\t"); + outs() << "\t optionalInstanceMethods " + << format("0x%" PRIx32, pc.optionalInstanceMethods) << "\n"; + outs() << "\t optionalClassMethods " + << format("0x%" PRIx32, pc.optionalClassMethods) << "\n"; + outs() << "\t instanceProperties " + << format("0x%" PRIx32, pc.instanceProperties) << "\n"; + p += sizeof(uint32_t); + offset += sizeof(uint32_t); + } +} + +static void print_indent(uint32_t indent) { + for (uint32_t i = 0; i < indent;) { + if (indent - i >= 8) { + outs() << "\t"; + i += 8; + } else { + for (uint32_t j = i; j < indent; j++) + outs() << " "; + return; + } + } +} + +static bool print_method_description_list(uint32_t p, uint32_t indent, + struct DisassembleInfo *info) { + uint32_t offset, left, xleft; + SectionRef S; + struct objc_method_description_list_t mdl; + struct objc_method_description_t md; + const char *r, *list, *name; + int32_t i; + + r = get_pointer_32(p, offset, left, S, info, true); + if (r == nullptr) + return true; + + outs() << "\n"; + if (left > sizeof(struct objc_method_description_list_t)) { + memcpy(&mdl, r, sizeof(struct objc_method_description_list_t)); + } else { + print_indent(indent); + outs() << " objc_method_description_list extends past end of the section\n"; + memset(&mdl, '\0', sizeof(struct objc_method_description_list_t)); + memcpy(&mdl, r, left); + } + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(mdl); + + print_indent(indent); + outs() << " count " << mdl.count << "\n"; + + list = r + sizeof(struct objc_method_description_list_t); + for (i = 0; i < mdl.count; i++) { + if ((i + 1) * sizeof(struct objc_method_description_t) > left) { + print_indent(indent); + outs() << " remaining list entries extend past the of the section\n"; + break; + } + print_indent(indent); + outs() << " list[" << i << "]\n"; + memcpy(&md, list + i * sizeof(struct objc_method_description_t), + sizeof(struct objc_method_description_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(md); + + print_indent(indent); + outs() << " name " << format("0x%08" PRIx32, md.name); + if (info->verbose) { + name = get_pointer_32(md.name, offset, xleft, S, info, true); + if (name != nullptr) + outs() << format(" %.*s", xleft, name); + else + outs() << " (not in an __OBJC section)"; + } + outs() << "\n"; + + print_indent(indent); + outs() << " types " << format("0x%08" PRIx32, md.types); + if (info->verbose) { + name = get_pointer_32(md.types, offset, xleft, S, info, true); + if (name != nullptr) + outs() << format(" %.*s", xleft, name); + else + outs() << " (not in an __OBJC section)"; + } + outs() << "\n"; + } + return false; +} + +static bool print_protocol_list(uint32_t p, uint32_t indent, + struct DisassembleInfo *info); + +static bool print_protocol(uint32_t p, uint32_t indent, + struct DisassembleInfo *info) { + uint32_t offset, left; + SectionRef S; + struct objc_protocol_t protocol; + const char *r, *name; + + r = get_pointer_32(p, offset, left, S, info, true); + if (r == nullptr) + return true; + + outs() << "\n"; + if (left >= sizeof(struct objc_protocol_t)) { + memcpy(&protocol, r, sizeof(struct objc_protocol_t)); + } else { + print_indent(indent); + outs() << " Protocol extends past end of the section\n"; + memset(&protocol, '\0', sizeof(struct objc_protocol_t)); + memcpy(&protocol, r, left); + } + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(protocol); + + print_indent(indent); + outs() << " isa " << format("0x%08" PRIx32, protocol.isa) + << "\n"; + + print_indent(indent); + outs() << " protocol_name " + << format("0x%08" PRIx32, protocol.protocol_name); + if (info->verbose) { + name = get_pointer_32(protocol.protocol_name, offset, left, S, info, true); + if (name != nullptr) + outs() << format(" %.*s", left, name); + else + outs() << " (not in an __OBJC section)"; + } + outs() << "\n"; + + print_indent(indent); + outs() << " protocol_list " + << format("0x%08" PRIx32, protocol.protocol_list); + if (print_protocol_list(protocol.protocol_list, indent + 4, info)) + outs() << " (not in an __OBJC section)\n"; + + print_indent(indent); + outs() << " instance_methods " + << format("0x%08" PRIx32, protocol.instance_methods); + if (print_method_description_list(protocol.instance_methods, indent, info)) + outs() << " (not in an __OBJC section)\n"; + + print_indent(indent); + outs() << " class_methods " + << format("0x%08" PRIx32, protocol.class_methods); + if (print_method_description_list(protocol.class_methods, indent, info)) + outs() << " (not in an __OBJC section)\n"; + + return false; +} + +static bool print_protocol_list(uint32_t p, uint32_t indent, + struct DisassembleInfo *info) { + uint32_t offset, left, l; + SectionRef S; + struct objc_protocol_list_t protocol_list; + const char *r, *list; + int32_t i; + + r = get_pointer_32(p, offset, left, S, info, true); + if (r == nullptr) + return true; + + outs() << "\n"; + if (left > sizeof(struct objc_protocol_list_t)) { + memcpy(&protocol_list, r, sizeof(struct objc_protocol_list_t)); + } else { + outs() << "\t\t objc_protocol_list_t extends past end of the section\n"; + memset(&protocol_list, '\0', sizeof(struct objc_protocol_list_t)); + memcpy(&protocol_list, r, left); + } + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(protocol_list); + + print_indent(indent); + outs() << " next " << format("0x%08" PRIx32, protocol_list.next) + << "\n"; + print_indent(indent); + outs() << " count " << protocol_list.count << "\n"; + + list = r + sizeof(struct objc_protocol_list_t); + for (i = 0; i < protocol_list.count; i++) { + if ((i + 1) * sizeof(uint32_t) > left) { + outs() << "\t\t remaining list entries extend past the of the section\n"; + break; + } + memcpy(&l, list + i * sizeof(uint32_t), sizeof(uint32_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + sys::swapByteOrder(l); + + print_indent(indent); + outs() << " list[" << i << "] " << format("0x%08" PRIx32, l); + if (print_protocol(l, indent, info)) + outs() << "(not in an __OBJC section)\n"; + } + return false; +} + +static void print_ivar_list64_t(uint64_t p, struct DisassembleInfo *info) { + struct ivar_list64_t il; + struct ivar64_t i; + const char *r; + uint32_t offset, xoffset, left, j; + SectionRef S, xS; + const char *name, *sym_name, *ivar_offset_p; + uint64_t ivar_offset, n_value; + + r = get_pointer_64(p, offset, left, S, info); + if (r == nullptr) + return; + memset(&il, '\0', sizeof(struct ivar_list64_t)); + if (left < sizeof(struct ivar_list64_t)) { + memcpy(&il, r, left); + outs() << " (ivar_list_t entends past the end of the section)\n"; + } else + memcpy(&il, r, sizeof(struct ivar_list64_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(il); + outs() << " entsize " << il.entsize << "\n"; + outs() << " count " << il.count << "\n"; + + p += sizeof(struct ivar_list64_t); + offset += sizeof(struct ivar_list64_t); + for (j = 0; j < il.count; j++) { + r = get_pointer_64(p, offset, left, S, info); + if (r == nullptr) + return; + memset(&i, '\0', sizeof(struct ivar64_t)); + if (left < sizeof(struct ivar64_t)) { + memcpy(&i, r, left); + outs() << " (ivar_t entends past the end of the section)\n"; + } else + memcpy(&i, r, sizeof(struct ivar64_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(i); + + outs() << "\t\t\t offset "; + sym_name = get_symbol_64(offset + offsetof(struct ivar64_t, offset), S, + info, n_value, i.offset); + if (n_value != 0) { + if (info->verbose && sym_name != nullptr) + outs() << sym_name; + else + outs() << format("0x%" PRIx64, n_value); + if (i.offset != 0) + outs() << " + " << format("0x%" PRIx64, i.offset); + } else + outs() << format("0x%" PRIx64, i.offset); + ivar_offset_p = get_pointer_64(i.offset + n_value, xoffset, left, xS, info); + if (ivar_offset_p != nullptr && left >= sizeof(*ivar_offset_p)) { + memcpy(&ivar_offset, ivar_offset_p, sizeof(ivar_offset)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + sys::swapByteOrder(ivar_offset); + outs() << " " << ivar_offset << "\n"; + } else + outs() << "\n"; + + outs() << "\t\t\t name "; + sym_name = get_symbol_64(offset + offsetof(struct ivar64_t, name), S, info, + n_value, i.name); + if (n_value != 0) { + if (info->verbose && sym_name != nullptr) + outs() << sym_name; + else + outs() << format("0x%" PRIx64, n_value); + if (i.name != 0) + outs() << " + " << format("0x%" PRIx64, i.name); + } else + outs() << format("0x%" PRIx64, i.name); + name = get_pointer_64(i.name + n_value, xoffset, left, xS, info); + if (name != nullptr) + outs() << format(" %.*s", left, name); + outs() << "\n"; + + outs() << "\t\t\t type "; + sym_name = get_symbol_64(offset + offsetof(struct ivar64_t, type), S, info, + n_value, i.name); + name = get_pointer_64(i.type + n_value, xoffset, left, xS, info); + if (n_value != 0) { + if (info->verbose && sym_name != nullptr) + outs() << sym_name; + else + outs() << format("0x%" PRIx64, n_value); + if (i.type != 0) + outs() << " + " << format("0x%" PRIx64, i.type); + } else + outs() << format("0x%" PRIx64, i.type); + if (name != nullptr) + outs() << format(" %.*s", left, name); + outs() << "\n"; + + outs() << "\t\t\talignment " << i.alignment << "\n"; + outs() << "\t\t\t size " << i.size << "\n"; + + p += sizeof(struct ivar64_t); + offset += sizeof(struct ivar64_t); + } +} + +static void print_ivar_list32_t(uint32_t p, struct DisassembleInfo *info) { + struct ivar_list32_t il; + struct ivar32_t i; + const char *r; + uint32_t offset, xoffset, left, j; + SectionRef S, xS; + const char *name, *ivar_offset_p; + uint32_t ivar_offset; + + r = get_pointer_32(p, offset, left, S, info); + if (r == nullptr) + return; + memset(&il, '\0', sizeof(struct ivar_list32_t)); + if (left < sizeof(struct ivar_list32_t)) { + memcpy(&il, r, left); + outs() << " (ivar_list_t entends past the end of the section)\n"; + } else + memcpy(&il, r, sizeof(struct ivar_list32_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(il); + outs() << " entsize " << il.entsize << "\n"; + outs() << " count " << il.count << "\n"; + + p += sizeof(struct ivar_list32_t); + offset += sizeof(struct ivar_list32_t); + for (j = 0; j < il.count; j++) { + r = get_pointer_32(p, offset, left, S, info); + if (r == nullptr) + return; + memset(&i, '\0', sizeof(struct ivar32_t)); + if (left < sizeof(struct ivar32_t)) { + memcpy(&i, r, left); + outs() << " (ivar_t entends past the end of the section)\n"; + } else + memcpy(&i, r, sizeof(struct ivar32_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(i); + + outs() << "\t\t\t offset " << format("0x%" PRIx32, i.offset); + ivar_offset_p = get_pointer_32(i.offset, xoffset, left, xS, info); + if (ivar_offset_p != nullptr && left >= sizeof(*ivar_offset_p)) { + memcpy(&ivar_offset, ivar_offset_p, sizeof(ivar_offset)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + sys::swapByteOrder(ivar_offset); + outs() << " " << ivar_offset << "\n"; + } else + outs() << "\n"; + + outs() << "\t\t\t name " << format("0x%" PRIx32, i.name); + name = get_pointer_32(i.name, xoffset, left, xS, info); + if (name != nullptr) + outs() << format(" %.*s", left, name); + outs() << "\n"; + + outs() << "\t\t\t type " << format("0x%" PRIx32, i.type); + name = get_pointer_32(i.type, xoffset, left, xS, info); + if (name != nullptr) + outs() << format(" %.*s", left, name); + outs() << "\n"; + + outs() << "\t\t\talignment " << i.alignment << "\n"; + outs() << "\t\t\t size " << i.size << "\n"; + + p += sizeof(struct ivar32_t); + offset += sizeof(struct ivar32_t); + } +} + +static void print_objc_property_list64(uint64_t p, + struct DisassembleInfo *info) { + struct objc_property_list64 opl; + struct objc_property64 op; + const char *r; + uint32_t offset, xoffset, left, j; + SectionRef S, xS; + const char *name, *sym_name; + uint64_t n_value; + + r = get_pointer_64(p, offset, left, S, info); + if (r == nullptr) + return; + memset(&opl, '\0', sizeof(struct objc_property_list64)); + if (left < sizeof(struct objc_property_list64)) { + memcpy(&opl, r, left); + outs() << " (objc_property_list entends past the end of the section)\n"; + } else + memcpy(&opl, r, sizeof(struct objc_property_list64)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(opl); + outs() << " entsize " << opl.entsize << "\n"; + outs() << " count " << opl.count << "\n"; + + p += sizeof(struct objc_property_list64); + offset += sizeof(struct objc_property_list64); + for (j = 0; j < opl.count; j++) { + r = get_pointer_64(p, offset, left, S, info); + if (r == nullptr) + return; + memset(&op, '\0', sizeof(struct objc_property64)); + if (left < sizeof(struct objc_property64)) { + memcpy(&op, r, left); + outs() << " (objc_property entends past the end of the section)\n"; + } else + memcpy(&op, r, sizeof(struct objc_property64)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(op); + + outs() << "\t\t\t name "; + sym_name = get_symbol_64(offset + offsetof(struct objc_property64, name), S, + info, n_value, op.name); + if (n_value != 0) { + if (info->verbose && sym_name != nullptr) + outs() << sym_name; + else + outs() << format("0x%" PRIx64, n_value); + if (op.name != 0) + outs() << " + " << format("0x%" PRIx64, op.name); + } else + outs() << format("0x%" PRIx64, op.name); + name = get_pointer_64(op.name + n_value, xoffset, left, xS, info); + if (name != nullptr) + outs() << format(" %.*s", left, name); + outs() << "\n"; + + outs() << "\t\t\tattributes "; + sym_name = + get_symbol_64(offset + offsetof(struct objc_property64, attributes), S, + info, n_value, op.attributes); + if (n_value != 0) { + if (info->verbose && sym_name != nullptr) + outs() << sym_name; + else + outs() << format("0x%" PRIx64, n_value); + if (op.attributes != 0) + outs() << " + " << format("0x%" PRIx64, op.attributes); + } else + outs() << format("0x%" PRIx64, op.attributes); + name = get_pointer_64(op.attributes + n_value, xoffset, left, xS, info); + if (name != nullptr) + outs() << format(" %.*s", left, name); + outs() << "\n"; + + p += sizeof(struct objc_property64); + offset += sizeof(struct objc_property64); + } +} + +static void print_objc_property_list32(uint32_t p, + struct DisassembleInfo *info) { + struct objc_property_list32 opl; + struct objc_property32 op; + const char *r; + uint32_t offset, xoffset, left, j; + SectionRef S, xS; + const char *name; + + r = get_pointer_32(p, offset, left, S, info); + if (r == nullptr) + return; + memset(&opl, '\0', sizeof(struct objc_property_list32)); + if (left < sizeof(struct objc_property_list32)) { + memcpy(&opl, r, left); + outs() << " (objc_property_list entends past the end of the section)\n"; + } else + memcpy(&opl, r, sizeof(struct objc_property_list32)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(opl); + outs() << " entsize " << opl.entsize << "\n"; + outs() << " count " << opl.count << "\n"; + + p += sizeof(struct objc_property_list32); + offset += sizeof(struct objc_property_list32); + for (j = 0; j < opl.count; j++) { + r = get_pointer_32(p, offset, left, S, info); + if (r == nullptr) + return; + memset(&op, '\0', sizeof(struct objc_property32)); + if (left < sizeof(struct objc_property32)) { + memcpy(&op, r, left); + outs() << " (objc_property entends past the end of the section)\n"; + } else + memcpy(&op, r, sizeof(struct objc_property32)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(op); + + outs() << "\t\t\t name " << format("0x%" PRIx32, op.name); + name = get_pointer_32(op.name, xoffset, left, xS, info); + if (name != nullptr) + outs() << format(" %.*s", left, name); + outs() << "\n"; + + outs() << "\t\t\tattributes " << format("0x%" PRIx32, op.attributes); + name = get_pointer_32(op.attributes, xoffset, left, xS, info); + if (name != nullptr) + outs() << format(" %.*s", left, name); + outs() << "\n"; + + p += sizeof(struct objc_property32); + offset += sizeof(struct objc_property32); + } +} + +static void print_class_ro64_t(uint64_t p, struct DisassembleInfo *info, + bool &is_meta_class) { + struct class_ro64_t cro; + const char *r; + uint32_t offset, xoffset, left; + SectionRef S, xS; + const char *name, *sym_name; + uint64_t n_value; + + r = get_pointer_64(p, offset, left, S, info); + if (r == nullptr || left < sizeof(struct class_ro64_t)) + return; + memset(&cro, '\0', sizeof(struct class_ro64_t)); + if (left < sizeof(struct class_ro64_t)) { + memcpy(&cro, r, left); + outs() << " (class_ro_t entends past the end of the section)\n"; + } else + memcpy(&cro, r, sizeof(struct class_ro64_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(cro); + outs() << " flags " << format("0x%" PRIx32, cro.flags); + if (cro.flags & RO_META) + outs() << " RO_META"; + if (cro.flags & RO_ROOT) + outs() << " RO_ROOT"; + if (cro.flags & RO_HAS_CXX_STRUCTORS) + outs() << " RO_HAS_CXX_STRUCTORS"; + outs() << "\n"; + outs() << " instanceStart " << cro.instanceStart << "\n"; + outs() << " instanceSize " << cro.instanceSize << "\n"; + outs() << " reserved " << format("0x%" PRIx32, cro.reserved) + << "\n"; + outs() << " ivarLayout " << format("0x%" PRIx64, cro.ivarLayout) + << "\n"; + print_layout_map64(cro.ivarLayout, info); + + outs() << " name "; + sym_name = get_symbol_64(offset + offsetof(struct class_ro64_t, name), S, + info, n_value, cro.name); + if (n_value != 0) { + if (info->verbose && sym_name != nullptr) + outs() << sym_name; + else + outs() << format("0x%" PRIx64, n_value); + if (cro.name != 0) + outs() << " + " << format("0x%" PRIx64, cro.name); + } else + outs() << format("0x%" PRIx64, cro.name); + name = get_pointer_64(cro.name + n_value, xoffset, left, xS, info); + if (name != nullptr) + outs() << format(" %.*s", left, name); + outs() << "\n"; + + outs() << " baseMethods "; + sym_name = get_symbol_64(offset + offsetof(struct class_ro64_t, baseMethods), + S, info, n_value, cro.baseMethods); + if (n_value != 0) { + if (info->verbose && sym_name != nullptr) + outs() << sym_name; + else + outs() << format("0x%" PRIx64, n_value); + if (cro.baseMethods != 0) + outs() << " + " << format("0x%" PRIx64, cro.baseMethods); + } else + outs() << format("0x%" PRIx64, cro.baseMethods); + outs() << " (struct method_list_t *)\n"; + if (cro.baseMethods + n_value != 0) + print_method_list64_t(cro.baseMethods + n_value, info, ""); + + outs() << " baseProtocols "; + sym_name = + get_symbol_64(offset + offsetof(struct class_ro64_t, baseProtocols), S, + info, n_value, cro.baseProtocols); + if (n_value != 0) { + if (info->verbose && sym_name != nullptr) + outs() << sym_name; + else + outs() << format("0x%" PRIx64, n_value); + if (cro.baseProtocols != 0) + outs() << " + " << format("0x%" PRIx64, cro.baseProtocols); + } else + outs() << format("0x%" PRIx64, cro.baseProtocols); + outs() << "\n"; + if (cro.baseProtocols + n_value != 0) + print_protocol_list64_t(cro.baseProtocols + n_value, info); + + outs() << " ivars "; + sym_name = get_symbol_64(offset + offsetof(struct class_ro64_t, ivars), S, + info, n_value, cro.ivars); + if (n_value != 0) { + if (info->verbose && sym_name != nullptr) + outs() << sym_name; + else + outs() << format("0x%" PRIx64, n_value); + if (cro.ivars != 0) + outs() << " + " << format("0x%" PRIx64, cro.ivars); + } else + outs() << format("0x%" PRIx64, cro.ivars); + outs() << "\n"; + if (cro.ivars + n_value != 0) + print_ivar_list64_t(cro.ivars + n_value, info); + + outs() << " weakIvarLayout "; + sym_name = + get_symbol_64(offset + offsetof(struct class_ro64_t, weakIvarLayout), S, + info, n_value, cro.weakIvarLayout); + if (n_value != 0) { + if (info->verbose && sym_name != nullptr) + outs() << sym_name; + else + outs() << format("0x%" PRIx64, n_value); + if (cro.weakIvarLayout != 0) + outs() << " + " << format("0x%" PRIx64, cro.weakIvarLayout); + } else + outs() << format("0x%" PRIx64, cro.weakIvarLayout); + outs() << "\n"; + print_layout_map64(cro.weakIvarLayout + n_value, info); + + outs() << " baseProperties "; + sym_name = + get_symbol_64(offset + offsetof(struct class_ro64_t, baseProperties), S, + info, n_value, cro.baseProperties); + if (n_value != 0) { + if (info->verbose && sym_name != nullptr) + outs() << sym_name; + else + outs() << format("0x%" PRIx64, n_value); + if (cro.baseProperties != 0) + outs() << " + " << format("0x%" PRIx64, cro.baseProperties); + } else + outs() << format("0x%" PRIx64, cro.baseProperties); + outs() << "\n"; + if (cro.baseProperties + n_value != 0) + print_objc_property_list64(cro.baseProperties + n_value, info); + + is_meta_class = (cro.flags & RO_META) ? true : false; +} + +static void print_class_ro32_t(uint32_t p, struct DisassembleInfo *info, + bool &is_meta_class) { + struct class_ro32_t cro; + const char *r; + uint32_t offset, xoffset, left; + SectionRef S, xS; + const char *name; + + r = get_pointer_32(p, offset, left, S, info); + if (r == nullptr) + return; + memset(&cro, '\0', sizeof(struct class_ro32_t)); + if (left < sizeof(struct class_ro32_t)) { + memcpy(&cro, r, left); + outs() << " (class_ro_t entends past the end of the section)\n"; + } else + memcpy(&cro, r, sizeof(struct class_ro32_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(cro); + outs() << " flags " << format("0x%" PRIx32, cro.flags); + if (cro.flags & RO_META) + outs() << " RO_META"; + if (cro.flags & RO_ROOT) + outs() << " RO_ROOT"; + if (cro.flags & RO_HAS_CXX_STRUCTORS) + outs() << " RO_HAS_CXX_STRUCTORS"; + outs() << "\n"; + outs() << " instanceStart " << cro.instanceStart << "\n"; + outs() << " instanceSize " << cro.instanceSize << "\n"; + outs() << " ivarLayout " << format("0x%" PRIx32, cro.ivarLayout) + << "\n"; + print_layout_map32(cro.ivarLayout, info); + + outs() << " name " << format("0x%" PRIx32, cro.name); + name = get_pointer_32(cro.name, xoffset, left, xS, info); + if (name != nullptr) + outs() << format(" %.*s", left, name); + outs() << "\n"; + + outs() << " baseMethods " + << format("0x%" PRIx32, cro.baseMethods) + << " (struct method_list_t *)\n"; + if (cro.baseMethods != 0) + print_method_list32_t(cro.baseMethods, info, ""); + + outs() << " baseProtocols " + << format("0x%" PRIx32, cro.baseProtocols) << "\n"; + if (cro.baseProtocols != 0) + print_protocol_list32_t(cro.baseProtocols, info); + outs() << " ivars " << format("0x%" PRIx32, cro.ivars) + << "\n"; + if (cro.ivars != 0) + print_ivar_list32_t(cro.ivars, info); + outs() << " weakIvarLayout " + << format("0x%" PRIx32, cro.weakIvarLayout) << "\n"; + print_layout_map32(cro.weakIvarLayout, info); + outs() << " baseProperties " + << 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; +} + +static void print_class64_t(uint64_t p, struct DisassembleInfo *info) { + struct class64_t c; + const char *r; + uint32_t offset, left; + SectionRef S; + const char *name; + uint64_t isa_n_value, n_value; + + r = get_pointer_64(p, offset, left, S, info); + if (r == nullptr || left < sizeof(struct class64_t)) + return; + memset(&c, '\0', sizeof(struct class64_t)); + if (left < sizeof(struct class64_t)) { + memcpy(&c, r, left); + outs() << " (class_t entends past the end of the section)\n"; + } else + memcpy(&c, r, sizeof(struct class64_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(c); + + outs() << " isa " << format("0x%" PRIx64, c.isa); + name = get_symbol_64(offset + offsetof(struct class64_t, isa), S, info, + isa_n_value, c.isa); + if (name != nullptr) + outs() << " " << name; + outs() << "\n"; + + outs() << " superclass " << format("0x%" PRIx64, c.superclass); + name = get_symbol_64(offset + offsetof(struct class64_t, superclass), S, info, + n_value, c.superclass); + if (name != nullptr) + outs() << " " << name; + outs() << "\n"; + + outs() << " cache " << format("0x%" PRIx64, c.cache); + name = get_symbol_64(offset + offsetof(struct class64_t, cache), S, info, + n_value, c.cache); + if (name != nullptr) + outs() << " " << name; + outs() << "\n"; + + outs() << " vtable " << format("0x%" PRIx64, c.vtable); + name = get_symbol_64(offset + offsetof(struct class64_t, vtable), S, info, + n_value, c.vtable); + if (name != nullptr) + outs() << " " << name; + outs() << "\n"; + + name = get_symbol_64(offset + offsetof(struct class64_t, data), S, info, + n_value, c.data); + outs() << " data "; + if (n_value != 0) { + if (info->verbose && name != nullptr) + outs() << name; + else + outs() << format("0x%" PRIx64, n_value); + if (c.data != 0) + outs() << " + " << format("0x%" PRIx64, c.data); + } else + outs() << format("0x%" PRIx64, c.data); + outs() << " (struct class_ro_t *)"; + + // This is a Swift class if some of the low bits of the pointer are set. + if ((c.data + n_value) & 0x7) + outs() << " Swift class"; + outs() << "\n"; + bool is_meta_class; + print_class_ro64_t((c.data + n_value) & ~0x7, info, is_meta_class); + + if (is_meta_class == false) { + outs() << "Meta Class\n"; + print_class64_t(c.isa + isa_n_value, info); + } +} + +static void print_class32_t(uint32_t p, struct DisassembleInfo *info) { + struct class32_t c; + const char *r; + uint32_t offset, left; + SectionRef S; + const char *name; + + r = get_pointer_32(p, offset, left, S, info); + if (r == nullptr) + return; + memset(&c, '\0', sizeof(struct class32_t)); + if (left < sizeof(struct class32_t)) { + memcpy(&c, r, left); + outs() << " (class_t entends past the end of the section)\n"; + } else + memcpy(&c, r, sizeof(struct class32_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(c); + + outs() << " isa " << format("0x%" PRIx32, c.isa); + name = + get_symbol_32(offset + offsetof(struct class32_t, isa), S, info, c.isa); + if (name != nullptr) + outs() << " " << name; + outs() << "\n"; + + outs() << " superclass " << format("0x%" PRIx32, c.superclass); + name = get_symbol_32(offset + offsetof(struct class32_t, superclass), S, info, + c.superclass); + if (name != nullptr) + outs() << " " << name; + outs() << "\n"; + + outs() << " cache " << format("0x%" PRIx32, c.cache); + name = get_symbol_32(offset + offsetof(struct class32_t, cache), S, info, + c.cache); + if (name != nullptr) + outs() << " " << name; + outs() << "\n"; + + outs() << " vtable " << format("0x%" PRIx32, c.vtable); + name = get_symbol_32(offset + offsetof(struct class32_t, vtable), S, info, + c.vtable); + if (name != nullptr) + outs() << " " << name; + outs() << "\n"; + + name = + get_symbol_32(offset + offsetof(struct class32_t, data), S, info, c.data); + outs() << " data " << format("0x%" PRIx32, c.data) + << " (struct class_ro_t *)"; + + // This is a Swift class if some of the low bits of the pointer are set. + if (c.data & 0x3) + outs() << " Swift class"; + outs() << "\n"; + bool is_meta_class; + print_class_ro32_t(c.data & ~0x3, info, is_meta_class); + + if (is_meta_class == false) { + outs() << "Meta Class\n"; + print_class32_t(c.isa, info); + } +} + +static void print_objc_class_t(struct objc_class_t *objc_class, + struct DisassembleInfo *info) { + uint32_t offset, left, xleft; + const char *name, *p, *ivar_list; + SectionRef S; + int32_t i; + struct objc_ivar_list_t objc_ivar_list; + struct objc_ivar_t ivar; + + outs() << "\t\t isa " << format("0x%08" PRIx32, objc_class->isa); + if (info->verbose && CLS_GETINFO(objc_class, CLS_META)) { + name = get_pointer_32(objc_class->isa, offset, left, S, info, true); + if (name != nullptr) + outs() << format(" %.*s", left, name); + else + outs() << " (not in an __OBJC section)"; + } + outs() << "\n"; + + outs() << "\t super_class " + << format("0x%08" PRIx32, objc_class->super_class); + if (info->verbose) { + name = get_pointer_32(objc_class->super_class, offset, left, S, info, true); + if (name != nullptr) + outs() << format(" %.*s", left, name); + else + outs() << " (not in an __OBJC section)"; + } + outs() << "\n"; + + outs() << "\t\t name " << format("0x%08" PRIx32, objc_class->name); + if (info->verbose) { + name = get_pointer_32(objc_class->name, offset, left, S, info, true); + if (name != nullptr) + outs() << format(" %.*s", left, name); + else + outs() << " (not in an __OBJC section)"; + } + outs() << "\n"; + + outs() << "\t\t version " << format("0x%08" PRIx32, objc_class->version) + << "\n"; + + outs() << "\t\t info " << format("0x%08" PRIx32, objc_class->info); + if (info->verbose) { + if (CLS_GETINFO(objc_class, CLS_CLASS)) + outs() << " CLS_CLASS"; + else if (CLS_GETINFO(objc_class, CLS_META)) + outs() << " CLS_META"; + } + outs() << "\n"; + + outs() << "\t instance_size " + << format("0x%08" PRIx32, objc_class->instance_size) << "\n"; + + p = get_pointer_32(objc_class->ivars, offset, left, S, info, true); + outs() << "\t\t ivars " << format("0x%08" PRIx32, objc_class->ivars); + if (p != nullptr) { + if (left > sizeof(struct objc_ivar_list_t)) { + outs() << "\n"; + memcpy(&objc_ivar_list, p, sizeof(struct objc_ivar_list_t)); + } else { + outs() << " (entends past the end of the section)\n"; + memset(&objc_ivar_list, '\0', sizeof(struct objc_ivar_list_t)); + memcpy(&objc_ivar_list, p, left); + } + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(objc_ivar_list); + outs() << "\t\t ivar_count " << objc_ivar_list.ivar_count << "\n"; + ivar_list = p + sizeof(struct objc_ivar_list_t); + for (i = 0; i < objc_ivar_list.ivar_count; i++) { + if ((i + 1) * sizeof(struct objc_ivar_t) > left) { + outs() << "\t\t remaining ivar's extend past the of the section\n"; + break; + } + memcpy(&ivar, ivar_list + i * sizeof(struct objc_ivar_t), + sizeof(struct objc_ivar_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(ivar); + + outs() << "\t\t\tivar_name " << format("0x%08" PRIx32, ivar.ivar_name); + if (info->verbose) { + name = get_pointer_32(ivar.ivar_name, offset, xleft, S, info, true); + if (name != nullptr) + outs() << format(" %.*s", xleft, name); + else + outs() << " (not in an __OBJC section)"; + } + outs() << "\n"; + + outs() << "\t\t\tivar_type " << format("0x%08" PRIx32, ivar.ivar_type); + if (info->verbose) { + name = get_pointer_32(ivar.ivar_type, offset, xleft, S, info, true); + if (name != nullptr) + outs() << format(" %.*s", xleft, name); + else + outs() << " (not in an __OBJC section)"; + } + outs() << "\n"; + + outs() << "\t\t ivar_offset " + << format("0x%08" PRIx32, ivar.ivar_offset) << "\n"; + } + } else { + outs() << " (not in an __OBJC section)\n"; + } + + outs() << "\t\t methods " << format("0x%08" PRIx32, objc_class->methodLists); + if (print_method_list(objc_class->methodLists, info)) + outs() << " (not in an __OBJC section)\n"; + + outs() << "\t\t cache " << format("0x%08" PRIx32, objc_class->cache) + << "\n"; + + outs() << "\t\tprotocols " << format("0x%08" PRIx32, objc_class->protocols); + if (print_protocol_list(objc_class->protocols, 16, info)) + outs() << " (not in an __OBJC section)\n"; +} + +static void print_objc_objc_category_t(struct objc_category_t *objc_category, + struct DisassembleInfo *info) { + uint32_t offset, left; + const char *name; + SectionRef S; + + outs() << "\t category name " + << format("0x%08" PRIx32, objc_category->category_name); + if (info->verbose) { + name = get_pointer_32(objc_category->category_name, offset, left, S, info, + true); + if (name != nullptr) + outs() << format(" %.*s", left, name); + else + outs() << " (not in an __OBJC section)"; + } + outs() << "\n"; + + outs() << "\t\t class name " + << format("0x%08" PRIx32, objc_category->class_name); + if (info->verbose) { + name = + get_pointer_32(objc_category->class_name, offset, left, S, info, true); + if (name != nullptr) + outs() << format(" %.*s", left, name); + else + outs() << " (not in an __OBJC section)"; + } + outs() << "\n"; + + outs() << "\t instance methods " + << format("0x%08" PRIx32, objc_category->instance_methods); + if (print_method_list(objc_category->instance_methods, info)) + outs() << " (not in an __OBJC section)\n"; + + outs() << "\t class methods " + << format("0x%08" PRIx32, objc_category->class_methods); + if (print_method_list(objc_category->class_methods, info)) + outs() << " (not in an __OBJC section)\n"; +} + +static void print_category64_t(uint64_t p, struct DisassembleInfo *info) { + struct category64_t c; + const char *r; + uint32_t offset, xoffset, left; + SectionRef S, xS; + const char *name, *sym_name; + uint64_t n_value; + + r = get_pointer_64(p, offset, left, S, info); + if (r == nullptr) + return; + memset(&c, '\0', sizeof(struct category64_t)); + if (left < sizeof(struct category64_t)) { + memcpy(&c, r, left); + outs() << " (category_t entends past the end of the section)\n"; + } else + memcpy(&c, r, sizeof(struct category64_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(c); + + outs() << " name "; + sym_name = get_symbol_64(offset + offsetof(struct category64_t, name), S, + info, n_value, c.name); + if (n_value != 0) { + if (info->verbose && sym_name != nullptr) + outs() << sym_name; + else + outs() << format("0x%" PRIx64, n_value); + if (c.name != 0) + outs() << " + " << format("0x%" PRIx64, c.name); + } else + outs() << format("0x%" PRIx64, c.name); + name = get_pointer_64(c.name + n_value, xoffset, left, xS, info); + if (name != nullptr) + outs() << format(" %.*s", left, name); + outs() << "\n"; + + outs() << " cls "; + sym_name = get_symbol_64(offset + offsetof(struct category64_t, cls), S, info, + n_value, c.cls); + if (n_value != 0) { + if (info->verbose && sym_name != nullptr) + outs() << sym_name; + else + outs() << format("0x%" PRIx64, n_value); + if (c.cls != 0) + outs() << " + " << format("0x%" PRIx64, c.cls); + } else + outs() << format("0x%" PRIx64, c.cls); + outs() << "\n"; + if (c.cls + n_value != 0) + print_class64_t(c.cls + n_value, info); + + outs() << " instanceMethods "; + sym_name = + get_symbol_64(offset + offsetof(struct category64_t, instanceMethods), S, + info, n_value, c.instanceMethods); + if (n_value != 0) { + if (info->verbose && sym_name != nullptr) + outs() << sym_name; + else + outs() << format("0x%" PRIx64, n_value); + if (c.instanceMethods != 0) + outs() << " + " << format("0x%" PRIx64, c.instanceMethods); + } else + outs() << format("0x%" PRIx64, c.instanceMethods); + outs() << "\n"; + if (c.instanceMethods + n_value != 0) + print_method_list64_t(c.instanceMethods + n_value, info, ""); + + outs() << " classMethods "; + sym_name = get_symbol_64(offset + offsetof(struct category64_t, classMethods), + S, info, n_value, c.classMethods); + if (n_value != 0) { + if (info->verbose && sym_name != nullptr) + outs() << sym_name; + else + outs() << format("0x%" PRIx64, n_value); + if (c.classMethods != 0) + outs() << " + " << format("0x%" PRIx64, c.classMethods); + } else + outs() << format("0x%" PRIx64, c.classMethods); + outs() << "\n"; + if (c.classMethods + n_value != 0) + print_method_list64_t(c.classMethods + n_value, info, ""); + + outs() << " protocols "; + sym_name = get_symbol_64(offset + offsetof(struct category64_t, protocols), S, + info, n_value, c.protocols); + if (n_value != 0) { + if (info->verbose && sym_name != nullptr) + outs() << sym_name; + else + outs() << format("0x%" PRIx64, n_value); + if (c.protocols != 0) + outs() << " + " << format("0x%" PRIx64, c.protocols); + } else + outs() << format("0x%" PRIx64, c.protocols); + outs() << "\n"; + if (c.protocols + n_value != 0) + print_protocol_list64_t(c.protocols + n_value, info); + + outs() << "instanceProperties "; + sym_name = + get_symbol_64(offset + offsetof(struct category64_t, instanceProperties), + S, info, n_value, c.instanceProperties); + if (n_value != 0) { + if (info->verbose && sym_name != nullptr) + outs() << sym_name; + else + outs() << format("0x%" PRIx64, n_value); + if (c.instanceProperties != 0) + outs() << " + " << format("0x%" PRIx64, c.instanceProperties); + } else + outs() << format("0x%" PRIx64, c.instanceProperties); + outs() << "\n"; + if (c.instanceProperties + n_value != 0) + print_objc_property_list64(c.instanceProperties + n_value, info); +} + +static void print_category32_t(uint32_t p, struct DisassembleInfo *info) { + struct category32_t c; + const char *r; + uint32_t offset, left; + SectionRef S, xS; + const char *name; + + r = get_pointer_32(p, offset, left, S, info); + if (r == nullptr) + return; + memset(&c, '\0', sizeof(struct category32_t)); + if (left < sizeof(struct category32_t)) { + memcpy(&c, r, left); + outs() << " (category_t entends past the end of the section)\n"; + } else + memcpy(&c, r, sizeof(struct category32_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(c); + + outs() << " name " << format("0x%" PRIx32, c.name); + name = get_symbol_32(offset + offsetof(struct category32_t, name), S, info, + c.name); + if (name != NULL) + outs() << " " << name; + outs() << "\n"; + + outs() << " cls " << format("0x%" PRIx32, c.cls) << "\n"; + if (c.cls != 0) + print_class32_t(c.cls, info); + outs() << " instanceMethods " << format("0x%" PRIx32, c.instanceMethods) + << "\n"; + if (c.instanceMethods != 0) + print_method_list32_t(c.instanceMethods, info, ""); + outs() << " classMethods " << format("0x%" PRIx32, c.classMethods) + << "\n"; + if (c.classMethods != 0) + print_method_list32_t(c.classMethods, info, ""); + outs() << " protocols " << format("0x%" PRIx32, c.protocols) << "\n"; + if (c.protocols != 0) + print_protocol_list32_t(c.protocols, info); + outs() << "instanceProperties " << format("0x%" PRIx32, c.instanceProperties) + << "\n"; + if (c.instanceProperties != 0) + print_objc_property_list32(c.instanceProperties, info); +} + +static void print_message_refs64(SectionRef S, struct DisassembleInfo *info) { + uint32_t i, left, offset, xoffset; + uint64_t p, n_value; + struct message_ref64 mr; + const char *name, *sym_name; + const char *r; + SectionRef xS; + + if (S == SectionRef()) + return; + + StringRef SectName; + S.getName(SectName); + DataRefImpl Ref = S.getRawDataRefImpl(); + StringRef SegName = info->O->getSectionFinalSegmentName(Ref); + outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; + offset = 0; + for (i = 0; i < S.getSize(); i += sizeof(struct message_ref64)) { + p = S.getAddress() + i; + r = get_pointer_64(p, offset, left, S, info); + if (r == nullptr) + return; + memset(&mr, '\0', sizeof(struct message_ref64)); + if (left < sizeof(struct message_ref64)) { + memcpy(&mr, r, left); + outs() << " (message_ref entends past the end of the section)\n"; + } else + memcpy(&mr, r, sizeof(struct message_ref64)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(mr); + + outs() << " imp "; + name = get_symbol_64(offset + offsetof(struct message_ref64, imp), S, info, + n_value, mr.imp); + if (n_value != 0) { + outs() << format("0x%" PRIx64, n_value) << " "; + if (mr.imp != 0) + outs() << "+ " << format("0x%" PRIx64, mr.imp) << " "; + } else + outs() << format("0x%" PRIx64, mr.imp) << " "; + if (name != nullptr) + outs() << " " << name; + outs() << "\n"; + + outs() << " sel "; + sym_name = get_symbol_64(offset + offsetof(struct message_ref64, sel), S, + info, n_value, mr.sel); + if (n_value != 0) { + if (info->verbose && sym_name != nullptr) + outs() << sym_name; + else + outs() << format("0x%" PRIx64, n_value); + if (mr.sel != 0) + outs() << " + " << format("0x%" PRIx64, mr.sel); + } else + outs() << format("0x%" PRIx64, mr.sel); + name = get_pointer_64(mr.sel + n_value, xoffset, left, xS, info); + if (name != nullptr) + outs() << format(" %.*s", left, name); + outs() << "\n"; + + offset += sizeof(struct message_ref64); + } +} + +static void print_message_refs32(SectionRef S, struct DisassembleInfo *info) { + uint32_t i, left, offset, xoffset, p; + struct message_ref32 mr; + const char *name, *r; + SectionRef xS; + + if (S == SectionRef()) + return; + + StringRef SectName; + S.getName(SectName); + DataRefImpl Ref = S.getRawDataRefImpl(); + StringRef SegName = info->O->getSectionFinalSegmentName(Ref); + outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; + offset = 0; + for (i = 0; i < S.getSize(); i += sizeof(struct message_ref64)) { + p = S.getAddress() + i; + r = get_pointer_32(p, offset, left, S, info); + if (r == nullptr) + return; + memset(&mr, '\0', sizeof(struct message_ref32)); + if (left < sizeof(struct message_ref32)) { + memcpy(&mr, r, left); + outs() << " (message_ref entends past the end of the section)\n"; + } else + memcpy(&mr, r, sizeof(struct message_ref32)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(mr); + + outs() << " imp " << format("0x%" PRIx32, mr.imp); + name = get_symbol_32(offset + offsetof(struct message_ref32, imp), S, info, + mr.imp); + if (name != nullptr) + outs() << " " << name; + outs() << "\n"; + + outs() << " sel " << format("0x%" PRIx32, mr.sel); + name = get_pointer_32(mr.sel, xoffset, left, xS, info); + if (name != nullptr) + outs() << " " << name; + outs() << "\n"; + + offset += sizeof(struct message_ref32); + } +} + +static void print_image_info64(SectionRef S, struct DisassembleInfo *info) { + uint32_t left, offset, swift_version; + uint64_t p; + struct objc_image_info64 o; + const char *r; + + StringRef SectName; + S.getName(SectName); + DataRefImpl Ref = S.getRawDataRefImpl(); + StringRef SegName = info->O->getSectionFinalSegmentName(Ref); + outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; + p = S.getAddress(); + r = get_pointer_64(p, offset, left, S, info); + if (r == nullptr) + return; + memset(&o, '\0', sizeof(struct objc_image_info64)); + if (left < sizeof(struct objc_image_info64)) { + memcpy(&o, r, left); + outs() << " (objc_image_info entends past the end of the section)\n"; + } else + memcpy(&o, r, sizeof(struct objc_image_info64)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(o); + outs() << " version " << o.version << "\n"; + outs() << " flags " << format("0x%" PRIx32, o.flags); + if (o.flags & OBJC_IMAGE_IS_REPLACEMENT) + outs() << " OBJC_IMAGE_IS_REPLACEMENT"; + if (o.flags & OBJC_IMAGE_SUPPORTS_GC) + outs() << " OBJC_IMAGE_SUPPORTS_GC"; + swift_version = (o.flags >> 8) & 0xff; + if (swift_version != 0) { + if (swift_version == 1) + outs() << " Swift 1.0"; + else if (swift_version == 2) + outs() << " Swift 1.1"; + else + outs() << " unknown future Swift version (" << swift_version << ")"; + } + outs() << "\n"; +} + +static void print_image_info32(SectionRef S, struct DisassembleInfo *info) { + uint32_t left, offset, swift_version, p; + struct objc_image_info32 o; + const char *r; + + StringRef SectName; + S.getName(SectName); + DataRefImpl Ref = S.getRawDataRefImpl(); + StringRef SegName = info->O->getSectionFinalSegmentName(Ref); + outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; + p = S.getAddress(); + r = get_pointer_32(p, offset, left, S, info); + if (r == nullptr) + return; + memset(&o, '\0', sizeof(struct objc_image_info32)); + if (left < sizeof(struct objc_image_info32)) { + memcpy(&o, r, left); + outs() << " (objc_image_info entends past the end of the section)\n"; + } else + memcpy(&o, r, sizeof(struct objc_image_info32)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(o); + outs() << " version " << o.version << "\n"; + outs() << " flags " << format("0x%" PRIx32, o.flags); + if (o.flags & OBJC_IMAGE_IS_REPLACEMENT) + outs() << " OBJC_IMAGE_IS_REPLACEMENT"; + if (o.flags & OBJC_IMAGE_SUPPORTS_GC) + outs() << " OBJC_IMAGE_SUPPORTS_GC"; + swift_version = (o.flags >> 8) & 0xff; + if (swift_version != 0) { + if (swift_version == 1) + outs() << " Swift 1.0"; + else if (swift_version == 2) + outs() << " Swift 1.1"; + else + outs() << " unknown future Swift version (" << swift_version << ")"; + } + outs() << "\n"; +} + +static void print_image_info(SectionRef S, struct DisassembleInfo *info) { + uint32_t left, offset, p; + struct imageInfo_t o; + const char *r; + + StringRef SectName; + S.getName(SectName); + DataRefImpl Ref = S.getRawDataRefImpl(); + StringRef SegName = info->O->getSectionFinalSegmentName(Ref); + outs() << "Contents of (" << SegName << "," << SectName << ") section\n"; + p = S.getAddress(); + r = get_pointer_32(p, offset, left, S, info); + if (r == nullptr) + return; + memset(&o, '\0', sizeof(struct imageInfo_t)); + if (left < sizeof(struct imageInfo_t)) { + memcpy(&o, r, left); + outs() << " (imageInfo entends past the end of the section)\n"; + } else + memcpy(&o, r, sizeof(struct imageInfo_t)); + if (info->O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(o); + outs() << " version " << o.version << "\n"; + outs() << " flags " << format("0x%" PRIx32, o.flags); + if (o.flags & 0x1) + outs() << " F&C"; + if (o.flags & 0x2) + outs() << " GC"; + if (o.flags & 0x4) + outs() << " GC-only"; + else + outs() << " RR"; + outs() << "\n"; +} + +static void printObjc2_64bit_MetaData(MachOObjectFile *O, bool verbose) { + SymbolAddressMap AddrMap; + if (verbose) + CreateSymbolAddressMap(O, &AddrMap); + + std::vector<SectionRef> Sections; + for (const SectionRef &Section : O->sections()) { + StringRef SectName; + Section.getName(SectName); + Sections.push_back(Section); + } + + struct DisassembleInfo info; + // Set up the block of info used by the Symbolizer call backs. + info.verbose = verbose; + info.O = O; + info.AddrMap = &AddrMap; + info.Sections = &Sections; + info.class_name = nullptr; + info.selector_name = nullptr; + info.method = nullptr; + info.demangled_name = nullptr; + info.bindtable = nullptr; + 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); + } + + 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); + } + + 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); + } + + 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); + } + + 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); + } + + 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); + } + + 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); + } + + if (info.bindtable != nullptr) + delete info.bindtable; +} + +static void printObjc2_32bit_MetaData(MachOObjectFile *O, bool verbose) { + SymbolAddressMap AddrMap; + if (verbose) + CreateSymbolAddressMap(O, &AddrMap); + + std::vector<SectionRef> Sections; + for (const SectionRef &Section : O->sections()) { + StringRef SectName; + Section.getName(SectName); + Sections.push_back(Section); + } + + struct DisassembleInfo info; + // Set up the block of info used by the Symbolizer call backs. + info.verbose = verbose; + info.O = O; + info.AddrMap = &AddrMap; + info.Sections = &Sections; + info.class_name = nullptr; + info.selector_name = nullptr; + info.method = nullptr; + info.demangled_name = nullptr; + info.bindtable = nullptr; + 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_32("class", CL, O, &info, print_class32_t); + } else { + const SectionRef CL = get_section(O, "__DATA", "__objc_classlist"); + info.S = CL; + walk_pointer_list_32("class", CL, O, &info, print_class32_t); + } + + const SectionRef CR = get_section(O, "__OBJC2", "__class_refs"); + if (CR != SectionRef()) { + info.S = CR; + walk_pointer_list_32("class refs", CR, O, &info, nullptr); + } else { + const SectionRef CR = get_section(O, "__DATA", "__objc_classrefs"); + info.S = CR; + walk_pointer_list_32("class refs", CR, O, &info, nullptr); + } + + const SectionRef SR = get_section(O, "__OBJC2", "__super_refs"); + if (SR != SectionRef()) { + info.S = SR; + walk_pointer_list_32("super refs", SR, O, &info, nullptr); + } else { + const SectionRef SR = get_section(O, "__DATA", "__objc_superrefs"); + info.S = SR; + walk_pointer_list_32("super refs", SR, O, &info, nullptr); + } + + const SectionRef CA = get_section(O, "__OBJC2", "__category_list"); + if (CA != SectionRef()) { + info.S = CA; + walk_pointer_list_32("category", CA, O, &info, print_category32_t); + } else { + const SectionRef CA = get_section(O, "__DATA", "__objc_catlist"); + info.S = CA; + walk_pointer_list_32("category", CA, O, &info, print_category32_t); + } + + const SectionRef PL = get_section(O, "__OBJC2", "__protocol_list"); + if (PL != SectionRef()) { + info.S = PL; + walk_pointer_list_32("protocol", PL, O, &info, nullptr); + } else { + const SectionRef PL = get_section(O, "__DATA", "__objc_protolist"); + info.S = PL; + walk_pointer_list_32("protocol", PL, O, &info, nullptr); + } + + const SectionRef MR = get_section(O, "__OBJC2", "__message_refs"); + if (MR != SectionRef()) { + info.S = MR; + print_message_refs32(MR, &info); + } else { + const SectionRef MR = get_section(O, "__DATA", "__objc_msgrefs"); + info.S = MR; + print_message_refs32(MR, &info); + } + + const SectionRef II = get_section(O, "__OBJC2", "__image_info"); + if (II != SectionRef()) { + info.S = II; + print_image_info32(II, &info); + } else { + const SectionRef II = get_section(O, "__DATA", "__objc_imageinfo"); + info.S = II; + print_image_info32(II, &info); + } +} + +static bool printObjc1_32bit_MetaData(MachOObjectFile *O, bool verbose) { + uint32_t i, j, p, offset, xoffset, left, defs_left, def; + const char *r, *name, *defs; + struct objc_module_t module; + SectionRef S, xS; + struct objc_symtab_t symtab; + struct objc_class_t objc_class; + struct objc_category_t objc_category; + + outs() << "Objective-C segment\n"; + S = get_section(O, "__OBJC", "__module_info"); + if (S == SectionRef()) + return false; + + SymbolAddressMap AddrMap; + if (verbose) + CreateSymbolAddressMap(O, &AddrMap); + + std::vector<SectionRef> Sections; + for (const SectionRef &Section : O->sections()) { + StringRef SectName; + Section.getName(SectName); + Sections.push_back(Section); + } + + struct DisassembleInfo info; + // Set up the block of info used by the Symbolizer call backs. + info.verbose = verbose; + info.O = O; + info.AddrMap = &AddrMap; + info.Sections = &Sections; + info.class_name = nullptr; + info.selector_name = nullptr; + info.method = nullptr; + info.demangled_name = nullptr; + info.bindtable = nullptr; + info.adrp_addr = 0; + info.adrp_inst = 0; + + for (i = 0; i < S.getSize(); i += sizeof(struct objc_module_t)) { + p = S.getAddress() + i; + r = get_pointer_32(p, offset, left, S, &info, true); + if (r == nullptr) + return true; + memset(&module, '\0', sizeof(struct objc_module_t)); + if (left < sizeof(struct objc_module_t)) { + memcpy(&module, r, left); + outs() << " (module extends past end of __module_info section)\n"; + } else + memcpy(&module, r, sizeof(struct objc_module_t)); + if (O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(module); + + outs() << "Module " << format("0x%" PRIx32, p) << "\n"; + outs() << " version " << module.version << "\n"; + outs() << " size " << module.size << "\n"; + outs() << " name "; + name = get_pointer_32(module.name, xoffset, left, xS, &info, true); + if (name != nullptr) + outs() << format("%.*s", left, name); + else + outs() << format("0x%08" PRIx32, module.name) + << "(not in an __OBJC section)"; + outs() << "\n"; + + r = get_pointer_32(module.symtab, xoffset, left, xS, &info, true); + if (module.symtab == 0 || r == nullptr) { + outs() << " symtab " << format("0x%08" PRIx32, module.symtab) + << " (not in an __OBJC section)\n"; + continue; + } + outs() << " symtab " << format("0x%08" PRIx32, module.symtab) << "\n"; + memset(&symtab, '\0', sizeof(struct objc_symtab_t)); + defs_left = 0; + defs = nullptr; + if (left < sizeof(struct objc_symtab_t)) { + memcpy(&symtab, r, left); + outs() << "\tsymtab extends past end of an __OBJC section)\n"; + } else { + memcpy(&symtab, r, sizeof(struct objc_symtab_t)); + if (left > sizeof(struct objc_symtab_t)) { + defs_left = left - sizeof(struct objc_symtab_t); + defs = r + sizeof(struct objc_symtab_t); + } + } + if (O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(symtab); + + outs() << "\tsel_ref_cnt " << symtab.sel_ref_cnt << "\n"; + r = get_pointer_32(symtab.refs, xoffset, left, xS, &info, true); + outs() << "\trefs " << format("0x%08" PRIx32, symtab.refs); + if (r == nullptr) + outs() << " (not in an __OBJC section)"; + outs() << "\n"; + outs() << "\tcls_def_cnt " << symtab.cls_def_cnt << "\n"; + outs() << "\tcat_def_cnt " << symtab.cat_def_cnt << "\n"; + if (symtab.cls_def_cnt > 0) + outs() << "\tClass Definitions\n"; + for (j = 0; j < symtab.cls_def_cnt; j++) { + if ((j + 1) * sizeof(uint32_t) > defs_left) { + outs() << "\t(remaining class defs entries entends past the end of the " + << "section)\n"; + break; + } + memcpy(&def, defs + j * sizeof(uint32_t), sizeof(uint32_t)); + if (O->isLittleEndian() != sys::IsLittleEndianHost) + sys::swapByteOrder(def); + + r = get_pointer_32(def, xoffset, left, xS, &info, true); + outs() << "\tdefs[" << j << "] " << format("0x%08" PRIx32, def); + if (r != nullptr) { + if (left > sizeof(struct objc_class_t)) { + outs() << "\n"; + memcpy(&objc_class, r, sizeof(struct objc_class_t)); + } else { + outs() << " (entends past the end of the section)\n"; + memset(&objc_class, '\0', sizeof(struct objc_class_t)); + memcpy(&objc_class, r, left); + } + if (O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(objc_class); + print_objc_class_t(&objc_class, &info); + } else { + outs() << "(not in an __OBJC section)\n"; + } + + if (CLS_GETINFO(&objc_class, CLS_CLASS)) { + outs() << "\tMeta Class"; + r = get_pointer_32(objc_class.isa, xoffset, left, xS, &info, true); + if (r != nullptr) { + if (left > sizeof(struct objc_class_t)) { + outs() << "\n"; + memcpy(&objc_class, r, sizeof(struct objc_class_t)); + } else { + outs() << " (entends past the end of the section)\n"; + memset(&objc_class, '\0', sizeof(struct objc_class_t)); + memcpy(&objc_class, r, left); + } + if (O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(objc_class); + print_objc_class_t(&objc_class, &info); + } else { + outs() << "(not in an __OBJC section)\n"; + } + } + } + if (symtab.cat_def_cnt > 0) + outs() << "\tCategory Definitions\n"; + for (j = 0; j < symtab.cat_def_cnt; j++) { + if ((j + symtab.cls_def_cnt + 1) * sizeof(uint32_t) > defs_left) { + outs() << "\t(remaining category defs entries entends past the end of " + << "the section)\n"; + break; + } + memcpy(&def, defs + (j + symtab.cls_def_cnt) * sizeof(uint32_t), + sizeof(uint32_t)); + if (O->isLittleEndian() != sys::IsLittleEndianHost) + sys::swapByteOrder(def); + + r = get_pointer_32(def, xoffset, left, xS, &info, true); + outs() << "\tdefs[" << j + symtab.cls_def_cnt << "] " + << format("0x%08" PRIx32, def); + if (r != nullptr) { + if (left > sizeof(struct objc_category_t)) { + outs() << "\n"; + memcpy(&objc_category, r, sizeof(struct objc_category_t)); + } else { + outs() << " (entends past the end of the section)\n"; + memset(&objc_category, '\0', sizeof(struct objc_category_t)); + memcpy(&objc_category, r, left); + } + if (O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(objc_category); + print_objc_objc_category_t(&objc_category, &info); + } else { + outs() << "(not in an __OBJC section)\n"; + } + } + } + const SectionRef II = get_section(O, "__OBJC", "__image_info"); + if (II != SectionRef()) + print_image_info(II, &info); + + return true; +} + +static void DumpProtocolSection(MachOObjectFile *O, const char *sect, + uint32_t size, uint32_t addr) { + SymbolAddressMap AddrMap; + CreateSymbolAddressMap(O, &AddrMap); + + std::vector<SectionRef> Sections; + for (const SectionRef &Section : O->sections()) { + StringRef SectName; + Section.getName(SectName); + Sections.push_back(Section); + } + + struct DisassembleInfo info; + // Set up the block of info used by the Symbolizer call backs. + info.verbose = true; + info.O = O; + info.AddrMap = &AddrMap; + info.Sections = &Sections; + info.class_name = nullptr; + info.selector_name = nullptr; + info.method = nullptr; + info.demangled_name = nullptr; + info.bindtable = nullptr; + info.adrp_addr = 0; + info.adrp_inst = 0; + + const char *p; + struct objc_protocol_t protocol; + uint32_t left, paddr; + for (p = sect; p < sect + size; p += sizeof(struct objc_protocol_t)) { + memset(&protocol, '\0', sizeof(struct objc_protocol_t)); + left = size - (p - sect); + if (left < sizeof(struct objc_protocol_t)) { + outs() << "Protocol extends past end of __protocol section\n"; + memcpy(&protocol, p, left); + } else + memcpy(&protocol, p, sizeof(struct objc_protocol_t)); + if (O->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(protocol); + paddr = addr + (p - sect); + outs() << "Protocol " << format("0x%" PRIx32, paddr); + if (print_protocol(paddr, 0, &info)) + outs() << "(not in an __OBJC section)\n"; + } +} + +static void printObjcMetaData(MachOObjectFile *O, bool verbose) { + if (O->is64Bit()) + printObjc2_64bit_MetaData(O, verbose); + else { + MachO::mach_header H; + H = O->getHeader(); + if (H.cputype == MachO::CPU_TYPE_ARM) + printObjc2_32bit_MetaData(O, verbose); + else { + // This is the 32-bit non-arm cputype case. Which is normally + // the first Objective-C ABI. But it may be the case of a + // 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) + printObjc2_32bit_MetaData(O, verbose); + } + } +} + // GuessLiteralPointer returns a string which for the item in the Mach-O file // for the address passed in as ReferenceValue for printing as a comment with // the instruction and also returns the corresponding type of that item @@ -1705,9 +5642,10 @@ uint64_t get_objc2_64bit_selref(uint64_t ReferenceValue, // // If there is no item in the Mach-O file for the address passed in as // ReferenceValue nullptr is returned and ReferenceType is unchanged. -const char *GuessLiteralPointer(uint64_t ReferenceValue, uint64_t ReferencePC, - uint64_t *ReferenceType, - struct DisassembleInfo *info) { +static const char *GuessLiteralPointer(uint64_t ReferenceValue, + uint64_t ReferencePC, + uint64_t *ReferenceType, + struct DisassembleInfo *info) { // First see if there is an external relocation entry at the ReferencePC. uint64_t sect_addr = info->S.getAddress(); uint64_t sect_offset = ReferencePC - sect_addr; @@ -1749,7 +5687,7 @@ const char *GuessLiteralPointer(uint64_t ReferenceValue, uint64_t ReferencePC, bool classref, selref, msgref, cfstring; uint64_t pointer_value = GuessPointerPointer(ReferenceValue, info, classref, selref, msgref, cfstring); - if (classref == true && pointer_value == 0) { + if (classref && pointer_value == 0) { // Note the ReferenceValue is a pointer into the __objc_classrefs section. // And the pointer_value in that section is typically zero as it will be // set by dyld as part of the "bind information". @@ -1765,7 +5703,7 @@ const char *GuessLiteralPointer(uint64_t ReferenceValue, uint64_t ReferencePC, } } - if (classref == true) { + if (classref) { *ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_Class_Ref; const char *name = get_objc2_64bit_class_name(pointer_value, ReferenceValue, info); @@ -1776,13 +5714,13 @@ const char *GuessLiteralPointer(uint64_t ReferenceValue, uint64_t ReferencePC, return name; } - if (cfstring == true) { + if (cfstring) { *ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_CFString_Ref; const char *name = get_objc2_64bit_cfstring_name(ReferenceValue, info); return name; } - if (selref == true && pointer_value == 0) + if (selref && pointer_value == 0) pointer_value = get_objc2_64bit_selref(ReferenceValue, info); if (pointer_value != 0) @@ -1790,10 +5728,10 @@ const char *GuessLiteralPointer(uint64_t ReferenceValue, uint64_t ReferencePC, const char *name = GuessCstringPointer(ReferenceValue, info); if (name) { - if (pointer_value != 0 && selref == true) { + if (pointer_value != 0 && selref) { *ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_Selector_Ref; info->selector_name = name; - } else if (pointer_value != 0 && msgref == true) { + } else if (pointer_value != 0 && msgref) { info->class_name = nullptr; *ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_Message_Ref; info->selector_name = name; @@ -1841,19 +5779,20 @@ const char *GuessLiteralPointer(uint64_t ReferenceValue, uint64_t ReferencePC, // SymbolValue is checked to be an address of literal pointer, symbol pointer, // or an Objective-C meta data reference. If so the output ReferenceType is // set to correspond to that as well as setting the ReferenceName. -const char *SymbolizerSymbolLookUp(void *DisInfo, uint64_t ReferenceValue, - uint64_t *ReferenceType, - uint64_t ReferencePC, - const char **ReferenceName) { +static const char *SymbolizerSymbolLookUp(void *DisInfo, + uint64_t ReferenceValue, + uint64_t *ReferenceType, + uint64_t ReferencePC, + const char **ReferenceName) { struct DisassembleInfo *info = (struct DisassembleInfo *)DisInfo; // If no verbose symbolic information is wanted then just return nullptr. - if (info->verbose == false) { + if (!info->verbose) { *ReferenceName = nullptr; *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None; return nullptr; } - const char *SymbolName = GuessSymbolName(ReferenceValue, info); + const char *SymbolName = GuessSymbolName(ReferenceValue, info->AddrMap); if (*ReferenceType == LLVMDisassembler_ReferenceType_In_Branch) { *ReferenceName = GuessIndirectSymbol(ReferenceValue, info); @@ -2017,7 +5956,8 @@ static void emitComments(raw_svector_ostream &CommentStream, CommentStream.resync(); } -static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF) { +static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, + StringRef DisSegName, StringRef DisSectName) { const char *McpuDefault = nullptr; const Target *ThumbTarget = nullptr; const Target *TheTarget = GetTarget(MachOOF, &McpuDefault, &ThumbTarget); @@ -2059,12 +5999,12 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF) { if (RelInfo) { Symbolizer.reset(TheTarget->createMCSymbolizer( TripleName, SymbolizerGetOpInfo, SymbolizerSymbolLookUp, - &SymbolizerInfo, &Ctx, RelInfo.release())); + &SymbolizerInfo, &Ctx, std::move(RelInfo))); DisAsm->setSymbolizer(std::move(Symbolizer)); } int AsmPrinterVariant = AsmInfo->getAssemblerDialect(); std::unique_ptr<MCInstPrinter> IP(TheTarget->createMCInstPrinter( - AsmPrinterVariant, *AsmInfo, *InstrInfo, *MRI, *STI)); + Triple(TripleName), AsmPrinterVariant, *AsmInfo, *InstrInfo, *MRI)); // Set the display preference for hex vs. decimal immediates. IP->setPrintImmHex(PrintImmHex); // Comment stream and backing vector. @@ -2107,13 +6047,13 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF) { if (ThumbRelInfo) { ThumbSymbolizer.reset(ThumbTarget->createMCSymbolizer( ThumbTripleName, SymbolizerGetOpInfo, SymbolizerSymbolLookUp, - &ThumbSymbolizerInfo, PtrThumbCtx, ThumbRelInfo.release())); + &ThumbSymbolizerInfo, PtrThumbCtx, std::move(ThumbRelInfo))); ThumbDisAsm->setSymbolizer(std::move(ThumbSymbolizer)); } int ThumbAsmPrinterVariant = ThumbAsmInfo->getAssemblerDialect(); ThumbIP.reset(ThumbTarget->createMCInstPrinter( - ThumbAsmPrinterVariant, *ThumbAsmInfo, *ThumbInstrInfo, *ThumbMRI, - *ThumbSTI)); + Triple(ThumbTripleName), ThumbAsmPrinterVariant, *ThumbAsmInfo, + *ThumbInstrInfo, *ThumbMRI)); // Set the display preference for hex vs. decimal immediates. ThumbIP->setPrintImmHex(PrintImmHex); } @@ -2182,31 +6122,21 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF) { } // Setup the DIContext - diContext.reset(DIContext::getDWARFContext(*DbgObj)); + diContext.reset(new DWARFContextInMemory(*DbgObj)); } - // TODO: For now this only disassembles the (__TEXT,__text) section (see the - // checks in the code below at the top of this loop). It should allow a - // darwin otool(1) like -s option to disassemble any named segment & section - // that is marked as containing instructions with the attributes - // S_ATTR_PURE_INSTRUCTIONS or S_ATTR_SOME_INSTRUCTIONS in the flags field of - // the section structure. - outs() << "(__TEXT,__text) section\n"; + if (DumpSections.size() == 0) + outs() << "(" << DisSegName << "," << DisSectName << ") section\n"; for (unsigned SectIdx = 0; SectIdx != Sections.size(); SectIdx++) { - - bool SectIsText = Sections[SectIdx].isText(); - if (SectIsText == false) - continue; - StringRef SectName; - if (Sections[SectIdx].getName(SectName) || SectName != "__text") - continue; // Skip non-text sections + if (Sections[SectIdx].getName(SectName) || SectName != DisSectName) + continue; DataRefImpl DR = Sections[SectIdx].getRawDataRefImpl(); StringRef SegmentName = MachOOF->getSectionFinalSegmentName(DR); - if (SegmentName != "__TEXT") + if (SegmentName != DisSegName) continue; StringRef BytesStr; @@ -2234,6 +6164,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF) { // Create a map of symbol addresses to symbol names for use by // the SymbolizerSymbolLookUp() routine. SymbolAddressMap AddrMap; + bool DisSymNameFound = false; for (const SymbolRef &Symbol : MachOOF->symbols()) { SymbolRef::Type ST; Symbol.getType(ST); @@ -2244,10 +6175,16 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF) { StringRef SymName; Symbol.getName(SymName); AddrMap[Address] = SymName; + if (!DisSymName.empty() && DisSymName == SymName) + DisSymNameFound = true; } } + if (!DisSymName.empty() && !DisSymNameFound) { + outs() << "Can't find -dis-symname: " << DisSymName << "\n"; + return; + } // Set up the block of info used by the Symbolizer call backs. - SymbolizerInfo.verbose = true; + SymbolizerInfo.verbose = !NoSymbolicOperands; SymbolizerInfo.O = MachOOF; SymbolizerInfo.S = Sections[SectIdx]; SymbolizerInfo.AddrMap = &AddrMap; @@ -2260,7 +6197,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF) { SymbolizerInfo.adrp_addr = 0; SymbolizerInfo.adrp_inst = 0; // Same for the ThumbSymbolizer - ThumbSymbolizerInfo.verbose = true; + ThumbSymbolizerInfo.verbose = !NoSymbolicOperands; ThumbSymbolizerInfo.O = MachOOF; ThumbSymbolizerInfo.S = Sections[SectIdx]; ThumbSymbolizerInfo.AddrMap = &AddrMap; @@ -2288,6 +6225,10 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF) { if (!containsSym) continue; + // If we are only disassembling one symbol see if this is that symbol. + if (!DisSymName.empty() && DisSymName != SymName) + continue; + // Start at the address of the symbol relative to the section's address. uint64_t Start = 0; uint64_t SectionAddress = Sections[SectIdx].getAddress(); @@ -2328,13 +6269,15 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF) { MCInst Inst; uint64_t PC = SectAddress + Index; - if (FullLeadingAddr) { - if (MachOOF->is64Bit()) - outs() << format("%016" PRIx64, PC); - else - outs() << format("%08" PRIx64, PC); - } else { - outs() << format("%8" PRIx64 ":", PC); + if (!NoLeadingAddr) { + if (FullLeadingAddr) { + if (MachOOF->is64Bit()) + outs() << format("%016" PRIx64, PC); + else + outs() << format("%08" PRIx64, PC); + } else { + outs() << format("%8" PRIx64 ":", PC); + } } if (!NoShowRawInsn) outs() << "\t"; @@ -2351,9 +6294,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF) { DTI->second.getLength(Length); uint16_t Kind; DTI->second.getKind(Kind); - Size = DumpDataInCode(reinterpret_cast<const char *>(Bytes.data()) + - Index, - Length, Kind); + Size = DumpDataInCode(Bytes.data() + Index, Length, Kind); if ((Kind == MachO::DICE_KIND_JUMP_TABLE8) && (PC == (DTI->first + Length - 1)) && (Length & 1)) Size++; @@ -2372,16 +6313,15 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF) { DebugOut, Annotations); if (gotInst) { if (!NoShowRawInsn) { - DumpBytes(StringRef( - reinterpret_cast<const char *>(Bytes.data()) + Index, Size)); + DumpBytes(ArrayRef<uint8_t>(Bytes.data() + Index, Size)); } formatted_raw_ostream FormattedOS(outs()); Annotations.flush(); StringRef AnnotationsStr = Annotations.str(); if (isThumb) - ThumbIP->printInst(&Inst, FormattedOS, AnnotationsStr); + ThumbIP->printInst(&Inst, FormattedOS, AnnotationsStr, *ThumbSTI); else - IP->printInst(&Inst, FormattedOS, AnnotationsStr); + IP->printInst(&Inst, FormattedOS, AnnotationsStr, *STI); emitComments(CommentStream, CommentsToEmit, FormattedOS, *AsmInfo); // Print debug info. @@ -2426,21 +6366,21 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF) { uint64_t PC = SectAddress + Index; if (DisAsm->getInstruction(Inst, InstSize, Bytes.slice(Index), PC, DebugOut, nulls())) { - if (FullLeadingAddr) { - if (MachOOF->is64Bit()) - outs() << format("%016" PRIx64, PC); - else - outs() << format("%08" PRIx64, PC); - } else { - outs() << format("%8" PRIx64 ":", PC); + if (!NoLeadingAddr) { + if (FullLeadingAddr) { + if (MachOOF->is64Bit()) + outs() << format("%016" PRIx64, PC); + else + outs() << format("%08" PRIx64, PC); + } else { + outs() << format("%8" PRIx64 ":", PC); + } } if (!NoShowRawInsn) { outs() << "\t"; - DumpBytes( - StringRef(reinterpret_cast<const char *>(Bytes.data()) + Index, - InstSize)); + DumpBytes(ArrayRef<uint8_t>(Bytes.data() + Index, InstSize)); } - IP->printInst(&Inst, outs(), ""); + IP->printInst(&Inst, outs(), "", *STI); outs() << "\n"; } else { unsigned int Arch = MachOOF->getArch(); @@ -2544,7 +6484,7 @@ static void findUnwindRelocNameAddend(const MachOObjectFile *Obj, } auto RE = Obj->getRelocation(Reloc.getRawDataRefImpl()); - SectionRef RelocSection = Obj->getRelocationSection(RE); + SectionRef RelocSection = Obj->getAnyRelocationSection(RE); uint64_t SectionAddr = RelocSection.getAddress(); @@ -4588,8 +8528,8 @@ void llvm::printMachOFileHeader(const object::ObjectFile *Obj) { uint32_t ncmds = 0; uint32_t filetype = 0; uint32_t cputype = 0; - getAndPrintMachHeader(file, ncmds, filetype, cputype, true); - PrintLoadCommands(file, ncmds, filetype, cputype, true); + getAndPrintMachHeader(file, ncmds, filetype, cputype, !NonVerbose); + PrintLoadCommands(file, ncmds, filetype, cputype, !NonVerbose); } //===----------------------------------------------------------------------===// diff --git a/tools/llvm-objdump/Makefile b/tools/llvm-objdump/Makefile index 4616b78adb2e..7c165230dde4 100644 --- a/tools/llvm-objdump/Makefile +++ b/tools/llvm-objdump/Makefile @@ -9,7 +9,7 @@ LEVEL := ../.. TOOLNAME := llvm-objdump -LINK_COMPONENTS := all-targets DebugInfo MC MCParser MCDisassembler Object +LINK_COMPONENTS := all-targets DebugInfoDWARF MC MCParser MCDisassembler Object # This tool has no plugins, optimize startup time. TOOL_NO_EXPORTS := 1 diff --git a/tools/llvm-objdump/llvm-objdump.cpp b/tools/llvm-objdump/llvm-objdump.cpp index aff6272d2fa3..e442ac050fce 100644 --- a/tools/llvm-objdump/llvm-objdump.cpp +++ b/tools/llvm-objdump/llvm-objdump.cpp @@ -68,14 +68,14 @@ static cl::alias Disassembled("d", cl::desc("Alias for --disassemble"), cl::aliasopt(Disassemble)); -static cl::opt<bool> -Relocations("r", cl::desc("Display the relocation entries in the file")); +cl::opt<bool> +llvm::Relocations("r", cl::desc("Display the relocation entries in the file")); -static cl::opt<bool> -SectionContents("s", cl::desc("Display the content of each section")); +cl::opt<bool> +llvm::SectionContents("s", cl::desc("Display the content of each section")); -static cl::opt<bool> -SymbolTable("t", cl::desc("Display the symbol table")); +cl::opt<bool> +llvm::SymbolTable("t", cl::desc("Display the symbol table")); cl::opt<bool> llvm::ExportsTrie("exports-trie", cl::desc("Display mach-o exported symbols")); @@ -111,9 +111,9 @@ cl::opt<std::string> llvm::ArchName("arch-name", cl::desc("Target arch to disassemble for, " "see -version for available targets")); -static cl::opt<bool> -SectionHeaders("section-headers", cl::desc("Display summaries of the headers " - "for each section.")); +cl::opt<bool> +llvm::SectionHeaders("section-headers", cl::desc("Display summaries of the " + "headers for each section.")); static cl::alias SectionHeadersShort("headers", cl::desc("Alias for --section-headers"), cl::aliasopt(SectionHeaders)); @@ -132,8 +132,8 @@ llvm::NoShowRawInsn("no-show-raw-insn", cl::desc("When disassembling " "instructions, do not print " "the instruction bytes.")); -static cl::opt<bool> -UnwindInfo("unwind-info", cl::desc("Display unwind information")); +cl::opt<bool> +llvm::UnwindInfo("unwind-info", cl::desc("Display unwind information")); static cl::alias UnwindInfoShort("u", cl::desc("Alias for --unwind-info"), @@ -194,30 +194,17 @@ static const Target *getTarget(const ObjectFile *Obj = nullptr) { return TheTarget; } -void llvm::DumpBytes(StringRef bytes) { +void llvm::DumpBytes(ArrayRef<uint8_t> bytes) { static const char hex_rep[] = "0123456789abcdef"; - // FIXME: The real way to do this is to figure out the longest instruction - // and align to that size before printing. I'll fix this when I get - // around to outputting relocations. - // 15 is the longest x86 instruction - // 3 is for the hex rep of a byte + a space. - // 1 is for the null terminator. - enum { OutputSize = (15 * 3) + 1 }; - char output[OutputSize]; - - assert(bytes.size() <= 15 - && "DumpBytes only supports instructions of up to 15 bytes"); - memset(output, ' ', sizeof(output)); - unsigned index = 0; - for (StringRef::iterator i = bytes.begin(), - e = bytes.end(); i != e; ++i) { - output[index] = hex_rep[(*i & 0xF0) >> 4]; - output[index + 1] = hex_rep[*i & 0xF]; - index += 3; + SmallString<64> output; + + for (char i: bytes) { + output.push_back(hex_rep[(i & 0xF0) >> 4]); + output.push_back(hex_rep[i & 0xF]); + output.push_back(' '); } - output[sizeof(output) - 1] = 0; - outs() << output; + outs() << output.c_str(); } bool llvm::RelocAddressLess(RelocationRef a, RelocationRef b) { @@ -287,7 +274,7 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { int AsmPrinterVariant = AsmInfo->getAssemblerDialect(); std::unique_ptr<MCInstPrinter> IP(TheTarget->createMCInstPrinter( - AsmPrinterVariant, *AsmInfo, *MII, *MRI, *STI)); + Triple(TripleName), AsmPrinterVariant, *AsmInfo, *MII, *MRI)); if (!IP) { errs() << "error: no instruction printer for target " << TripleName << '\n'; @@ -412,10 +399,9 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { outs() << format("%8" PRIx64 ":", SectionAddr + Index); if (!NoShowRawInsn) { outs() << "\t"; - DumpBytes(StringRef( - reinterpret_cast<const char *>(Bytes.data()) + Index, Size)); + DumpBytes(ArrayRef<uint8_t>(Bytes.data() + Index, Size)); } - IP->printInst(&Inst, outs(), ""); + IP->printInst(&Inst, outs(), "", *STI); outs() << CommentStream.str(); Comments.clear(); outs() << "\n"; @@ -453,7 +439,7 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { } } -static void PrintRelocations(const ObjectFile *Obj) { +void llvm::PrintRelocations(const ObjectFile *Obj) { StringRef Fmt = Obj->getBytesInAddress() > 4 ? "%016" PRIx64 : "%08" PRIx64; // Regular objdump doesn't print relocations in non-relocatable object @@ -490,7 +476,7 @@ static void PrintRelocations(const ObjectFile *Obj) { } } -static void PrintSectionHeaders(const ObjectFile *Obj) { +void llvm::PrintSectionHeaders(const ObjectFile *Obj) { outs() << "Sections:\n" "Idx Name Size Address Type\n"; unsigned i = 0; @@ -511,7 +497,7 @@ static void PrintSectionHeaders(const ObjectFile *Obj) { } } -static void PrintSectionContents(const ObjectFile *Obj) { +void llvm::PrintSectionContents(const ObjectFile *Obj) { std::error_code EC; for (const SectionRef &Section : Obj->sections()) { StringRef Name; @@ -614,7 +600,7 @@ static void PrintCOFFSymbolTable(const COFFObjectFile *coff) { } } -static void PrintSymbolTable(const ObjectFile *o) { +void llvm::PrintSymbolTable(const ObjectFile *o) { outs() << "SYMBOL TABLE:\n"; if (const COFFObjectFile *coff = dyn_cast<const COFFObjectFile>(o)) { @@ -642,7 +628,16 @@ static void PrintSymbolTable(const ObjectFile *o) { bool Global = Flags & SymbolRef::SF_Global; bool Weak = Flags & SymbolRef::SF_Weak; bool Absolute = Flags & SymbolRef::SF_Absolute; - + bool Common = Flags & SymbolRef::SF_Common; + bool Hidden = Flags & SymbolRef::SF_Hidden; + + if (Common) { + uint32_t Alignment; + if (error(Symbol.getAlignment(Alignment))) + Alignment = 0; + Address = Size; + Size = Alignment; + } if (Address == UnknownAddressOrSize) Address = 0; if (Size == UnknownAddressOrSize) @@ -672,6 +667,8 @@ static void PrintSymbolTable(const ObjectFile *o) { << ' '; if (Absolute) { outs() << "*ABS*"; + } else if (Common) { + outs() << "*COM*"; } else if (Section == o->section_end()) { outs() << "*UND*"; } else { @@ -687,8 +684,11 @@ static void PrintSymbolTable(const ObjectFile *o) { outs() << SectionName; } outs() << '\t' - << format("%08" PRIx64 " ", Size) - << Name + << format("%08" PRIx64 " ", Size); + if (Hidden) { + outs() << ".hidden "; + } + outs() << Name << '\n'; } } @@ -892,7 +892,16 @@ int main(int argc, char **argv) { && !Bind && !LazyBind && !WeakBind - && !(UniversalHeaders && MachOOpt)) { + && !(UniversalHeaders && MachOOpt) + && !(ArchiveHeaders && MachOOpt) + && !(IndirectSymbols && MachOOpt) + && !(DataInCode && MachOOpt) + && !(LinkOptHints && MachOOpt) + && !(InfoPlist && MachOOpt) + && !(DylibsUsed && MachOOpt) + && !(DylibId && MachOOpt) + && !(ObjcMetaData && MachOOpt) + && !(DumpSections.size() != 0 && MachOOpt)) { cl::PrintHelpMessage(); return 2; } diff --git a/tools/llvm-objdump/llvm-objdump.h b/tools/llvm-objdump/llvm-objdump.h index f829dd1a71c3..bde72e004a1f 100644 --- a/tools/llvm-objdump/llvm-objdump.h +++ b/tools/llvm-objdump/llvm-objdump.h @@ -1,4 +1,3 @@ -//===-- llvm-objdump.h ----------------------------------------------------===// // // The LLVM Compiler Infrastructure // @@ -26,6 +25,8 @@ 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::opt<bool> Raw; extern cl::opt<bool> Disassemble; extern cl::opt<bool> NoShowRawInsn; extern cl::opt<bool> PrivateHeaders; @@ -35,11 +36,26 @@ extern cl::opt<bool> Bind; extern cl::opt<bool> LazyBind; extern cl::opt<bool> WeakBind; extern cl::opt<bool> UniversalHeaders; +extern cl::opt<bool> ArchiveHeaders; +extern cl::opt<bool> IndirectSymbols; +extern cl::opt<bool> DataInCode; +extern cl::opt<bool> LinkOptHints; +extern cl::opt<bool> InfoPlist; +extern cl::opt<bool> DylibsUsed; +extern cl::opt<bool> DylibId; +extern cl::opt<bool> ObjcMetaData; +extern cl::opt<std::string> DisSymName; +extern cl::opt<bool> NonVerbose; +extern cl::opt<bool> Relocations; +extern cl::opt<bool> SectionHeaders; +extern cl::opt<bool> SectionContents; +extern cl::opt<bool> SymbolTable; +extern cl::opt<bool> UnwindInfo; // Various helper functions. bool error(std::error_code ec); bool RelocAddressLess(object::RelocationRef a, object::RelocationRef b); -void DumpBytes(StringRef bytes); +void DumpBytes(ArrayRef<uint8_t> bytes); void ParseInputMachO(StringRef Filename); void printCOFFUnwindInfo(const object::COFFObjectFile* o); void printMachOUnwindInfo(const object::MachOObjectFile* o); @@ -56,6 +72,10 @@ void printRebaseTable(const object::ObjectFile *o); void printBindTable(const object::ObjectFile *o); void printLazyBindTable(const object::ObjectFile *o); void printWeakBindTable(const object::ObjectFile *o); +void PrintRelocations(const object::ObjectFile *o); +void PrintSectionHeaders(const object::ObjectFile *o); +void PrintSectionContents(const object::ObjectFile *o); +void PrintSymbolTable(const object::ObjectFile *o); } // end namespace llvm diff --git a/tools/llvm-pdbdump/BuiltinDumper.cpp b/tools/llvm-pdbdump/BuiltinDumper.cpp new file mode 100644 index 000000000000..d80829841f3a --- /dev/null +++ b/tools/llvm-pdbdump/BuiltinDumper.cpp @@ -0,0 +1,87 @@ +//===- BuiltinDumper.cpp ---------------------------------------- *- C++ *-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "BuiltinDumper.h" +#include "LinePrinter.h" +#include "llvm-pdbdump.h" + +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" + +using namespace llvm; + +BuiltinDumper::BuiltinDumper(LinePrinter &P) + : PDBSymDumper(false), Printer(P) {} + +void BuiltinDumper::start(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; + case PDB_BuiltinType::UInt: + WithColor(Printer, PDB_ColorItem::Type).get() << "unsigned"; + if (Symbol.getLength() == 8) + WithColor(Printer, PDB_ColorItem::Type).get() << " __int64"; + break; + case PDB_BuiltinType::Int: + if (Symbol.getLength() == 4) + WithColor(Printer, PDB_ColorItem::Type).get() << "int"; + else + WithColor(Printer, PDB_ColorItem::Type).get() << "__int64"; + break; + case PDB_BuiltinType::Char: + WithColor(Printer, PDB_ColorItem::Type).get() << "char"; + break; + case PDB_BuiltinType::WCharT: + WithColor(Printer, PDB_ColorItem::Type).get() << "wchar_t"; + break; + case PDB_BuiltinType::Void: + WithColor(Printer, PDB_ColorItem::Type).get() << "void"; + break; + case PDB_BuiltinType::Long: + WithColor(Printer, PDB_ColorItem::Type).get() << "long"; + break; + case PDB_BuiltinType::ULong: + WithColor(Printer, PDB_ColorItem::Type).get() << "unsigned long"; + break; + case PDB_BuiltinType::Bool: + WithColor(Printer, PDB_ColorItem::Type).get() << "bool"; + break; + case PDB_BuiltinType::Currency: + WithColor(Printer, PDB_ColorItem::Type).get() << "CURRENCY"; + break; + case PDB_BuiltinType::Date: + WithColor(Printer, PDB_ColorItem::Type).get() << "DATE"; + break; + case PDB_BuiltinType::Variant: + WithColor(Printer, PDB_ColorItem::Type).get() << "VARIANT"; + break; + case PDB_BuiltinType::Complex: + WithColor(Printer, PDB_ColorItem::Type).get() << "complex"; + break; + case PDB_BuiltinType::Bitfield: + WithColor(Printer, PDB_ColorItem::Type).get() << "bitfield"; + break; + case PDB_BuiltinType::BSTR: + WithColor(Printer, PDB_ColorItem::Type).get() << "BSTR"; + break; + case PDB_BuiltinType::HResult: + WithColor(Printer, PDB_ColorItem::Type).get() << "HRESULT"; + break; + case PDB_BuiltinType::BCD: + WithColor(Printer, PDB_ColorItem::Type).get() << "HRESULT"; + break; + default: + WithColor(Printer, PDB_ColorItem::Type).get() << "void"; + break; + } +} diff --git a/tools/llvm-pdbdump/BuiltinDumper.h b/tools/llvm-pdbdump/BuiltinDumper.h new file mode 100644 index 000000000000..8cf984a0ca61 --- /dev/null +++ b/tools/llvm-pdbdump/BuiltinDumper.h @@ -0,0 +1,30 @@ +//===- BuiltinDumper.h ---------------------------------------- *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMPDBDUMP_BUILTINDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_BUILTINDUMPER_H + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +namespace llvm { + +class LinePrinter; + +class BuiltinDumper : public PDBSymDumper { +public: + BuiltinDumper(LinePrinter &P); + + void start(const PDBSymbolTypeBuiltin &Symbol); + +private: + LinePrinter &Printer; +}; +} + +#endif diff --git a/tools/llvm-pdbdump/CMakeLists.txt b/tools/llvm-pdbdump/CMakeLists.txt new file mode 100644 index 000000000000..1907f917079a --- /dev/null +++ b/tools/llvm-pdbdump/CMakeLists.txt @@ -0,0 +1,18 @@ +set(LLVM_LINK_COMPONENTS + Support + DebugInfoPDB + ) + +add_llvm_tool(llvm-pdbdump + llvm-pdbdump.cpp + BuiltinDumper.cpp + ClassDefinitionDumper.cpp + CompilandDumper.cpp + EnumDumper.cpp + ExternalSymbolDumper.cpp + FunctionDumper.cpp + LinePrinter.cpp + TypeDumper.cpp + TypedefDumper.cpp + VariableDumper.cpp + ) diff --git a/tools/llvm-pdbdump/ClassDefinitionDumper.cpp b/tools/llvm-pdbdump/ClassDefinitionDumper.cpp new file mode 100644 index 000000000000..8abf3fa3912a --- /dev/null +++ b/tools/llvm-pdbdump/ClassDefinitionDumper.cpp @@ -0,0 +1,190 @@ +//===- ClassDefinitionDumper.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ClassDefinitionDumper.h" +#include "EnumDumper.h" +#include "FunctionDumper.h" +#include "LinePrinter.h" +#include "llvm-pdbdump.h" +#include "TypedefDumper.h" +#include "VariableDumper.h" + +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDBExtras.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBaseClass.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTable.h" +#include "llvm/Support/Format.h" + +using namespace llvm; + +ClassDefinitionDumper::ClassDefinitionDumper(LinePrinter &P) + : PDBSymDumper(true), Printer(P) {} + +void ClassDefinitionDumper::start(const PDBSymbolTypeUDT &Class) { + std::string Name = Class.getName(); + WithColor(Printer, PDB_ColorItem::Keyword).get() << Class.getUdtKind() << " "; + WithColor(Printer, PDB_ColorItem::Type).get() << Class.getName(); + + auto Bases = Class.findAllChildren<PDBSymbolTypeBaseClass>(); + if (Bases->getChildCount() > 0) { + Printer.Indent(); + Printer.NewLine(); + Printer << ":"; + uint32_t BaseIndex = 0; + while (auto Base = Bases->getNext()) { + Printer << " "; + WithColor(Printer, PDB_ColorItem::Keyword).get() << Base->getAccess(); + if (Base->isVirtualBaseClass()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << " virtual"; + WithColor(Printer, PDB_ColorItem::Type).get() << " " << Base->getName(); + if (++BaseIndex < Bases->getChildCount()) { + Printer.NewLine(); + Printer << ","; + } + } + Printer.Unindent(); + } + + Printer << " {"; + auto Children = Class.findAllChildren(); + if (Children->getChildCount() == 0) { + Printer << "}"; + return; + } + + // Try to dump symbols organized by member access level. Public members + // first, then protected, then private. This might be slow, so it's worth + // reconsidering the value of this if performance of large PDBs is a problem. + // NOTE: Access level of nested types is not recorded in the PDB, so we have + // a special case for them. + SymbolGroupByAccess Groups; + Groups.insert(std::make_pair(0, SymbolGroup())); + Groups.insert(std::make_pair((int)PDB_MemberAccess::Private, SymbolGroup())); + Groups.insert( + std::make_pair((int)PDB_MemberAccess::Protected, SymbolGroup())); + Groups.insert(std::make_pair((int)PDB_MemberAccess::Public, SymbolGroup())); + + while (auto Child = Children->getNext()) { + PDB_MemberAccess Access = Child->getRawSymbol().getAccess(); + if (isa<PDBSymbolTypeBaseClass>(*Child)) + continue; + + auto &AccessGroup = Groups.find((int)Access)->second; + + if (auto Func = dyn_cast<PDBSymbolFunc>(Child.get())) { + if (Func->isCompilerGenerated() && opts::ExcludeCompilerGenerated) + continue; + if (Func->getLength() == 0 && !Func->isPureVirtual() && + !Func->isIntroVirtualFunction()) + continue; + Child.release(); + AccessGroup.Functions.push_back(std::unique_ptr<PDBSymbolFunc>(Func)); + } else if (auto Data = dyn_cast<PDBSymbolData>(Child.get())) { + Child.release(); + AccessGroup.Data.push_back(std::unique_ptr<PDBSymbolData>(Data)); + } else { + AccessGroup.Unknown.push_back(std::move(Child)); + } + } + + int Count = 0; + Count += dumpAccessGroup((PDB_MemberAccess)0, Groups[0]); + Count += dumpAccessGroup(PDB_MemberAccess::Public, + Groups[(int)PDB_MemberAccess::Public]); + Count += dumpAccessGroup(PDB_MemberAccess::Protected, + Groups[(int)PDB_MemberAccess::Protected]); + Count += dumpAccessGroup(PDB_MemberAccess::Private, + Groups[(int)PDB_MemberAccess::Private]); + if (Count > 0) + Printer.NewLine(); + Printer << "}"; +} + +int ClassDefinitionDumper::dumpAccessGroup(PDB_MemberAccess Access, + const SymbolGroup &Group) { + if (Group.Functions.empty() && Group.Data.empty() && Group.Unknown.empty()) + return 0; + + int Count = 0; + if (Access == PDB_MemberAccess::Private) { + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Keyword).get() << "private"; + Printer << ":"; + } else if (Access == PDB_MemberAccess::Protected) { + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Keyword).get() << "protected"; + Printer << ":"; + } else if (Access == PDB_MemberAccess::Public) { + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Keyword).get() << "public"; + Printer << ":"; + } + Printer.Indent(); + for (auto iter = Group.Functions.begin(), end = Group.Functions.end(); + iter != end; ++iter) { + ++Count; + (*iter)->dump(*this); + } + for (auto iter = Group.Data.begin(), end = Group.Data.end(); iter != end; + ++iter) { + ++Count; + (*iter)->dump(*this); + } + for (auto iter = Group.Unknown.begin(), end = Group.Unknown.end(); + iter != end; ++iter) { + ++Count; + (*iter)->dump(*this); + } + Printer.Unindent(); + return Count; +} + +void ClassDefinitionDumper::dump(const PDBSymbolTypeBaseClass &Symbol) {} + +void ClassDefinitionDumper::dump(const PDBSymbolData &Symbol) { + VariableDumper Dumper(Printer); + Dumper.start(Symbol); +} + +void ClassDefinitionDumper::dump(const PDBSymbolFunc &Symbol) { + if (Printer.IsSymbolExcluded(Symbol.getName())) + return; + + Printer.NewLine(); + FunctionDumper Dumper(Printer); + Dumper.start(Symbol, FunctionDumper::PointerType::None); +} + +void ClassDefinitionDumper::dump(const PDBSymbolTypeVTable &Symbol) {} + +void ClassDefinitionDumper::dump(const PDBSymbolTypeEnum &Symbol) { + if (Printer.IsTypeExcluded(Symbol.getName())) + return; + + Printer.NewLine(); + EnumDumper Dumper(Printer); + Dumper.start(Symbol); +} + +void ClassDefinitionDumper::dump(const PDBSymbolTypeTypedef &Symbol) { + if (Printer.IsTypeExcluded(Symbol.getName())) + return; + + Printer.NewLine(); + TypedefDumper Dumper(Printer); + Dumper.start(Symbol); +} + +void ClassDefinitionDumper::dump(const PDBSymbolTypeUDT &Symbol) {} diff --git a/tools/llvm-pdbdump/ClassDefinitionDumper.h b/tools/llvm-pdbdump/ClassDefinitionDumper.h new file mode 100644 index 000000000000..5b48ba879cf7 --- /dev/null +++ b/tools/llvm-pdbdump/ClassDefinitionDumper.h @@ -0,0 +1,62 @@ +//===- ClassDefinitionDumper.h - --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMPDBDUMP_CLASSDEFINITIONDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_CLASSDEFINITIONDUMPER_H + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" + +#include <list> +#include <memory> +#include <unordered_map> + +namespace llvm { + +class LinePrinter; + +class ClassDefinitionDumper : public PDBSymDumper { +public: + ClassDefinitionDumper(LinePrinter &P); + + void start(const PDBSymbolTypeUDT &Exe); + + void dump(const PDBSymbolTypeBaseClass &Symbol) override; + void dump(const PDBSymbolData &Symbol) override; + void dump(const PDBSymbolTypeEnum &Symbol) override; + void dump(const PDBSymbolFunc &Symbol) override; + void dump(const PDBSymbolTypeTypedef &Symbol) override; + void dump(const PDBSymbolTypeUDT &Symbol) override; + void dump(const PDBSymbolTypeVTable &Symbol) override; + +private: + LinePrinter &Printer; + + struct SymbolGroup { + SymbolGroup() {} + SymbolGroup(SymbolGroup &&Other) { + Functions = std::move(Other.Functions); + Data = std::move(Other.Data); + Unknown = std::move(Other.Unknown); + } + + std::list<std::unique_ptr<PDBSymbolFunc>> Functions; + std::list<std::unique_ptr<PDBSymbolData>> Data; + std::list<std::unique_ptr<PDBSymbol>> Unknown; + SymbolGroup(const SymbolGroup &other) = delete; + SymbolGroup &operator=(const SymbolGroup &other) = delete; + }; + typedef std::unordered_map<int, SymbolGroup> SymbolGroupByAccess; + + int dumpAccessGroup(PDB_MemberAccess Access, const SymbolGroup &Group); +}; +} + +#endif diff --git a/tools/llvm-pdbdump/CompilandDumper.cpp b/tools/llvm-pdbdump/CompilandDumper.cpp new file mode 100644 index 000000000000..68ceb620627b --- /dev/null +++ b/tools/llvm-pdbdump/CompilandDumper.cpp @@ -0,0 +1,140 @@ +//===- CompilandDumper.cpp - llvm-pdbdump compiland symbol dumper *- C++ *-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CompilandDumper.h" +#include "LinePrinter.h" +#include "llvm-pdbdump.h" + +#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDBExtras.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugEnd.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugStart.h" +#include "llvm/DebugInfo/PDB/PDBSymbolLabel.h" +#include "llvm/DebugInfo/PDB/PDBSymbolThunk.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h" +#include "llvm/DebugInfo/PDB/PDBSymbolUnknown.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +#include "FunctionDumper.h" + +#include <utility> +#include <vector> + +using namespace llvm; + +CompilandDumper::CompilandDumper(LinePrinter &P) + : PDBSymDumper(true), Printer(P) {} + +void CompilandDumper::dump(const PDBSymbolCompilandDetails &Symbol) {} + +void CompilandDumper::dump(const PDBSymbolCompilandEnv &Symbol) {} + +void CompilandDumper::start(const PDBSymbolCompiland &Symbol, bool Children) { + std::string FullName = Symbol.getName(); + if (Printer.IsCompilandExcluded(FullName)) + return; + + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Path).get() << FullName; + if (!Children) + return; + + auto ChildrenEnum = Symbol.findAllChildren(); + Printer.Indent(); + while (auto Child = ChildrenEnum->getNext()) + Child->dump(*this); + Printer.Unindent(); +} + +void CompilandDumper::dump(const PDBSymbolData &Symbol) { + if (Printer.IsSymbolExcluded(Symbol.getName())) + return; + + Printer.NewLine(); + + switch (auto LocType = Symbol.getLocationType()) { + case PDB_LocType::Static: + Printer << "data: "; + WithColor(Printer, PDB_ColorItem::Address).get() + << "[" << format_hex(Symbol.getVirtualAddress(), 10) << "]"; + break; + case PDB_LocType::Constant: + Printer << "constant: "; + WithColor(Printer, PDB_ColorItem::LiteralValue).get() + << "[" << Symbol.getValue() << "]"; + break; + default: + Printer << "data(unexpected type=" << LocType << ")"; + } + + Printer << " "; + WithColor(Printer, PDB_ColorItem::Identifier).get() << Symbol.getName(); +} + +void CompilandDumper::dump(const PDBSymbolFunc &Symbol) { + if (Symbol.getLength() == 0) + return; + if (Printer.IsSymbolExcluded(Symbol.getName())) + return; + + Printer.NewLine(); + FunctionDumper Dumper(Printer); + Dumper.start(Symbol, FunctionDumper::PointerType::None); +} + +void CompilandDumper::dump(const PDBSymbolLabel &Symbol) { + if (Printer.IsSymbolExcluded(Symbol.getName())) + return; + + Printer.NewLine(); + Printer << "label "; + WithColor(Printer, PDB_ColorItem::Address).get() + << "[" << format_hex(Symbol.getVirtualAddress(), 10) << "] "; + WithColor(Printer, PDB_ColorItem::Identifier).get() << Symbol.getName(); +} + +void CompilandDumper::dump(const PDBSymbolThunk &Symbol) { + if (Printer.IsSymbolExcluded(Symbol.getName())) + return; + + Printer.NewLine(); + Printer << "thunk "; + PDB_ThunkOrdinal Ordinal = Symbol.getThunkOrdinal(); + uint64_t VA = Symbol.getVirtualAddress(); + if (Ordinal == PDB_ThunkOrdinal::TrampIncremental) { + uint64_t Target = Symbol.getTargetVirtualAddress(); + WithColor(Printer, PDB_ColorItem::Address).get() << format_hex(VA, 10); + Printer << " -> "; + WithColor(Printer, PDB_ColorItem::Address).get() << format_hex(Target, 10); + } else { + WithColor(Printer, PDB_ColorItem::Address).get() + << "[" << format_hex(VA, 10) << " - " + << format_hex(VA + Symbol.getLength(), 10) << "]"; + } + Printer << " ("; + WithColor(Printer, PDB_ColorItem::Register).get() << Ordinal; + Printer << ") "; + std::string Name = Symbol.getName(); + if (!Name.empty()) + WithColor(Printer, PDB_ColorItem::Identifier).get() << Name; +} + +void CompilandDumper::dump(const PDBSymbolTypeTypedef &Symbol) {} + +void CompilandDumper::dump(const PDBSymbolUnknown &Symbol) { + Printer.NewLine(); + Printer << "unknown (" << Symbol.getSymTag() << ")"; +} diff --git a/tools/llvm-pdbdump/CompilandDumper.h b/tools/llvm-pdbdump/CompilandDumper.h new file mode 100644 index 000000000000..0d1d27cd7a43 --- /dev/null +++ b/tools/llvm-pdbdump/CompilandDumper.h @@ -0,0 +1,39 @@ +//===- CompilandDumper.h - llvm-pdbdump compiland symbol dumper *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMPDBDUMP_COMPILANDDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_COMPILANDDUMPER_H + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +namespace llvm { + +class LinePrinter; + +class CompilandDumper : public PDBSymDumper { +public: + CompilandDumper(LinePrinter &P); + + void start(const PDBSymbolCompiland &Symbol, bool Children); + + void dump(const PDBSymbolCompilandDetails &Symbol) override; + void dump(const PDBSymbolCompilandEnv &Symbol) override; + void dump(const PDBSymbolData &Symbol) override; + void dump(const PDBSymbolFunc &Symbol) override; + void dump(const PDBSymbolLabel &Symbol) override; + void dump(const PDBSymbolThunk &Symbol) override; + void dump(const PDBSymbolTypeTypedef &Symbol) override; + void dump(const PDBSymbolUnknown &Symbol) override; + +private: + LinePrinter &Printer; +}; +} + +#endif diff --git a/tools/llvm-pdbdump/EnumDumper.cpp b/tools/llvm-pdbdump/EnumDumper.cpp new file mode 100644 index 000000000000..3514c39f6e03 --- /dev/null +++ b/tools/llvm-pdbdump/EnumDumper.cpp @@ -0,0 +1,52 @@ +//===- EnumDumper.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "EnumDumper.h" + +#include "BuiltinDumper.h" +#include "LinePrinter.h" +#include "llvm-pdbdump.h" + +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" + +using namespace llvm; + +EnumDumper::EnumDumper(LinePrinter &P) : PDBSymDumper(true), Printer(P) {} + +void EnumDumper::start(const PDBSymbolTypeEnum &Symbol) { + WithColor(Printer, PDB_ColorItem::Keyword).get() << "enum "; + WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName(); + if (!opts::NoEnumDefs) { + auto BuiltinType = Symbol.getUnderlyingType(); + if (BuiltinType->getBuiltinType() != PDB_BuiltinType::Int || + BuiltinType->getLength() != 4) { + Printer << " : "; + BuiltinDumper Dumper(Printer); + Dumper.start(*BuiltinType); + } + Printer << " {"; + Printer.Indent(); + auto EnumValues = Symbol.findAllChildren<PDBSymbolData>(); + while (auto EnumValue = EnumValues->getNext()) { + if (EnumValue->getDataKind() != PDB_DataKind::Constant) + continue; + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Identifier).get() + << EnumValue->getName(); + Printer << " = "; + WithColor(Printer, PDB_ColorItem::LiteralValue).get() + << EnumValue->getValue(); + } + Printer.Unindent(); + Printer.NewLine(); + Printer << "}"; + } +} diff --git a/tools/llvm-pdbdump/EnumDumper.h b/tools/llvm-pdbdump/EnumDumper.h new file mode 100644 index 000000000000..23de0614247f --- /dev/null +++ b/tools/llvm-pdbdump/EnumDumper.h @@ -0,0 +1,30 @@ +//===- EnumDumper.h - -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMPDBDUMP_ENUMDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_ENUMDUMPER_H + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +namespace llvm { + +class LinePrinter; + +class EnumDumper : public PDBSymDumper { +public: + EnumDumper(LinePrinter &P); + + void start(const PDBSymbolTypeEnum &Symbol); + +private: + LinePrinter &Printer; +}; +} + +#endif diff --git a/tools/llvm-pdbdump/ExternalSymbolDumper.cpp b/tools/llvm-pdbdump/ExternalSymbolDumper.cpp new file mode 100644 index 000000000000..c4e9f474880e --- /dev/null +++ b/tools/llvm-pdbdump/ExternalSymbolDumper.cpp @@ -0,0 +1,40 @@ +//===- ExternalSymbolDumper.cpp -------------------------------- *- C++ *-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ExternalSymbolDumper.h" +#include "LinePrinter.h" + +#include "llvm/DebugInfo/PDB/PDBSymbolExe.h" +#include "llvm/DebugInfo/PDB/PDBSymbolPublicSymbol.h" +#include "llvm/Support/Format.h" + +using namespace llvm; + +ExternalSymbolDumper::ExternalSymbolDumper(LinePrinter &P) + : PDBSymDumper(true), Printer(P) {} + +void ExternalSymbolDumper::start(const PDBSymbolExe &Symbol) { + auto Vars = Symbol.findAllChildren<PDBSymbolPublicSymbol>(); + while (auto Var = Vars->getNext()) + Var->dump(*this); +} + +void ExternalSymbolDumper::dump(const PDBSymbolPublicSymbol &Symbol) { + std::string LinkageName = Symbol.getName(); + if (Printer.IsSymbolExcluded(LinkageName)) + return; + + Printer.NewLine(); + uint64_t Addr = Symbol.getVirtualAddress(); + + Printer << "["; + WithColor(Printer, PDB_ColorItem::Address).get() << format_hex(Addr, 10); + Printer << "] "; + WithColor(Printer, PDB_ColorItem::Identifier).get() << LinkageName; +} diff --git a/tools/llvm-pdbdump/ExternalSymbolDumper.h b/tools/llvm-pdbdump/ExternalSymbolDumper.h new file mode 100644 index 000000000000..d77b09cdebf1 --- /dev/null +++ b/tools/llvm-pdbdump/ExternalSymbolDumper.h @@ -0,0 +1,32 @@ +//===- ExternalSymbolDumper.h --------------------------------- *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMPDBDUMP_EXTERNALSYMBOLDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_EXTERNALSYMBOLDUMPER_H + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +namespace llvm { + +class LinePrinter; + +class ExternalSymbolDumper : public PDBSymDumper { +public: + ExternalSymbolDumper(LinePrinter &P); + + void start(const PDBSymbolExe &Symbol); + + void dump(const PDBSymbolPublicSymbol &Symbol) override; + +private: + LinePrinter &Printer; +}; +} + +#endif diff --git a/tools/llvm-pdbdump/FunctionDumper.cpp b/tools/llvm-pdbdump/FunctionDumper.cpp new file mode 100644 index 000000000000..9584812e81a9 --- /dev/null +++ b/tools/llvm-pdbdump/FunctionDumper.cpp @@ -0,0 +1,254 @@ +//===- FunctionDumper.cpp ------------------------------------ *- C++ *-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "FunctionDumper.h" +#include "BuiltinDumper.h" +#include "LinePrinter.h" +#include "llvm-pdbdump.h" + +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugEnd.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugStart.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeArray.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionArg.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" +#include "llvm/Support/Format.h" + +using namespace llvm; + +namespace { +template <class T> +void dumpClassParentWithScopeOperator(const T &Symbol, LinePrinter &Printer, + llvm::FunctionDumper &Dumper) { + uint32_t ClassParentId = Symbol.getClassParentId(); + auto ClassParent = + Symbol.getSession().template getConcreteSymbolById<PDBSymbolTypeUDT>( + ClassParentId); + if (!ClassParent) + return; + + WithColor(Printer, PDB_ColorItem::Type).get() << ClassParent->getName(); + Printer << "::"; +} +} + +FunctionDumper::FunctionDumper(LinePrinter &P) + : PDBSymDumper(true), Printer(P) {} + +void FunctionDumper::start(const PDBSymbolTypeFunctionSig &Symbol, + const char *Name, PointerType Pointer) { + auto ReturnType = Symbol.getReturnType(); + ReturnType->dump(*this); + Printer << " "; + uint32_t ClassParentId = Symbol.getClassParentId(); + auto ClassParent = + Symbol.getSession().getConcreteSymbolById<PDBSymbolTypeUDT>( + ClassParentId); + + PDB_CallingConv CC = Symbol.getCallingConvention(); + bool ShouldDumpCallingConvention = true; + if ((ClassParent && CC == PDB_CallingConv::Thiscall) || + (!ClassParent && CC == PDB_CallingConv::NearStdcall)) { + ShouldDumpCallingConvention = false; + } + + if (Pointer == PointerType::None) { + if (ShouldDumpCallingConvention) + WithColor(Printer, PDB_ColorItem::Keyword).get() << CC << " "; + if (ClassParent) { + Printer << "("; + WithColor(Printer, PDB_ColorItem::Identifier).get() + << ClassParent->getName(); + Printer << "::)"; + } + } else { + Printer << "("; + if (ShouldDumpCallingConvention) + WithColor(Printer, PDB_ColorItem::Keyword).get() << CC << " "; + if (ClassParent) { + WithColor(Printer, PDB_ColorItem::Identifier).get() + << ClassParent->getName(); + Printer << "::"; + } + if (Pointer == PointerType::Reference) + Printer << "&"; + else + Printer << "*"; + if (Name) + WithColor(Printer, PDB_ColorItem::Identifier).get() << Name; + Printer << ")"; + } + + Printer << "("; + if (auto ChildEnum = Symbol.getArguments()) { + uint32_t Index = 0; + while (auto Arg = ChildEnum->getNext()) { + Arg->dump(*this); + if (++Index < ChildEnum->getChildCount()) + Printer << ", "; + } + } + Printer << ")"; + + if (Symbol.isConstType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << " const"; + if (Symbol.isVolatileType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << " volatile"; +} + +void FunctionDumper::start(const PDBSymbolFunc &Symbol, PointerType Pointer) { + uint64_t FuncStart = Symbol.getVirtualAddress(); + uint64_t FuncEnd = FuncStart + Symbol.getLength(); + + Printer << "func ["; + WithColor(Printer, PDB_ColorItem::Address).get() << format_hex(FuncStart, 10); + if (auto DebugStart = Symbol.findOneChild<PDBSymbolFuncDebugStart>()) { + uint64_t Prologue = DebugStart->getVirtualAddress() - FuncStart; + WithColor(Printer, PDB_ColorItem::Offset).get() << "+" << Prologue; + } + Printer << " - "; + WithColor(Printer, PDB_ColorItem::Address).get() << format_hex(FuncEnd, 10); + if (auto DebugEnd = Symbol.findOneChild<PDBSymbolFuncDebugEnd>()) { + uint64_t Epilogue = FuncEnd - DebugEnd->getVirtualAddress(); + WithColor(Printer, PDB_ColorItem::Offset).get() << "-" << Epilogue; + } + Printer << "] ("; + + if (Symbol.hasFramePointer()) { + WithColor(Printer, PDB_ColorItem::Register).get() + << Symbol.getLocalBasePointerRegisterId(); + } else { + WithColor(Printer, PDB_ColorItem::Register).get() << "FPO"; + } + Printer << ") "; + + if (Symbol.isVirtual() || Symbol.isPureVirtual()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << "virtual "; + + auto Signature = Symbol.getSignature(); + if (!Signature) { + WithColor(Printer, PDB_ColorItem::Identifier).get() << Symbol.getName(); + if (Pointer == PointerType::Pointer) + Printer << "*"; + else if (Pointer == FunctionDumper::PointerType::Reference) + Printer << "&"; + return; + } + + auto ReturnType = Signature->getReturnType(); + ReturnType->dump(*this); + Printer << " "; + + auto ClassParent = Symbol.getClassParent(); + PDB_CallingConv CC = Signature->getCallingConvention(); + if (Pointer != FunctionDumper::PointerType::None) + Printer << "("; + + if ((ClassParent && CC != PDB_CallingConv::Thiscall) || + (!ClassParent && CC != PDB_CallingConv::NearStdcall)) { + WithColor(Printer, PDB_ColorItem::Keyword).get() + << Signature->getCallingConvention() << " "; + } + WithColor(Printer, PDB_ColorItem::Identifier).get() << Symbol.getName(); + if (Pointer != FunctionDumper::PointerType::None) { + if (Pointer == PointerType::Pointer) + Printer << "*"; + else if (Pointer == FunctionDumper::PointerType::Reference) + Printer << "&"; + Printer << ")"; + } + + Printer << "("; + if (auto Arguments = Symbol.getArguments()) { + uint32_t Index = 0; + while (auto Arg = Arguments->getNext()) { + auto ArgType = Arg->getType(); + ArgType->dump(*this); + WithColor(Printer, PDB_ColorItem::Identifier).get() << " " + << Arg->getName(); + if (++Index < Arguments->getChildCount()) + Printer << ", "; + } + } + Printer << ")"; + if (Symbol.isConstType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << " const"; + if (Symbol.isVolatileType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << " volatile"; + if (Symbol.isPureVirtual()) + Printer << " = 0"; +} + +void FunctionDumper::dump(const PDBSymbolTypeArray &Symbol) { + uint32_t ElementTypeId = Symbol.getTypeId(); + auto ElementType = Symbol.getSession().getSymbolById(ElementTypeId); + if (!ElementType) + return; + + ElementType->dump(*this); + Printer << "["; + WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Symbol.getLength(); + Printer << "]"; +} + +void FunctionDumper::dump(const PDBSymbolTypeBuiltin &Symbol) { + BuiltinDumper Dumper(Printer); + Dumper.start(Symbol); +} + +void FunctionDumper::dump(const PDBSymbolTypeEnum &Symbol) { + dumpClassParentWithScopeOperator(Symbol, Printer, *this); + WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName(); +} + +void FunctionDumper::dump(const PDBSymbolTypeFunctionArg &Symbol) { + // PDBSymbolTypeFunctionArg is just a shim over the real argument. Just drill + // through to the real thing and dump it. + uint32_t TypeId = Symbol.getTypeId(); + auto Type = Symbol.getSession().getSymbolById(TypeId); + if (!Type) + return; + Type->dump(*this); +} + +void FunctionDumper::dump(const PDBSymbolTypeTypedef &Symbol) { + dumpClassParentWithScopeOperator(Symbol, Printer, *this); + WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName(); +} + +void FunctionDumper::dump(const PDBSymbolTypePointer &Symbol) { + uint32_t PointeeId = Symbol.getTypeId(); + auto PointeeType = Symbol.getSession().getSymbolById(PointeeId); + if (!PointeeType) + return; + + if (auto FuncSig = dyn_cast<PDBSymbolTypeFunctionSig>(PointeeType.get())) { + FunctionDumper NestedDumper(Printer); + PointerType Pointer = + Symbol.isReference() ? PointerType::Reference : PointerType::Pointer; + NestedDumper.start(*FuncSig, nullptr, Pointer); + } else { + if (Symbol.isConstType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << "const "; + if (Symbol.isVolatileType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << "volatile "; + PointeeType->dump(*this); + Printer << (Symbol.isReference() ? "&" : "*"); + } +} + +void FunctionDumper::dump(const PDBSymbolTypeUDT &Symbol) { + WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName(); +} diff --git a/tools/llvm-pdbdump/FunctionDumper.h b/tools/llvm-pdbdump/FunctionDumper.h new file mode 100644 index 000000000000..19a00145a1fd --- /dev/null +++ b/tools/llvm-pdbdump/FunctionDumper.h @@ -0,0 +1,42 @@ +//===- FunctionDumper.h --------------------------------------- *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMPDBDUMP_FUNCTIONDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_FUNCTIONDUMPER_H + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +namespace llvm { + +class LinePrinter; + +class FunctionDumper : public PDBSymDumper { +public: + FunctionDumper(LinePrinter &P); + + enum class PointerType { None, Pointer, Reference }; + + void start(const PDBSymbolTypeFunctionSig &Symbol, const char *Name, + PointerType Pointer); + void start(const PDBSymbolFunc &Symbol, PointerType Pointer); + + void dump(const PDBSymbolTypeArray &Symbol) override; + void dump(const PDBSymbolTypeBuiltin &Symbol) override; + void dump(const PDBSymbolTypeEnum &Symbol) override; + void dump(const PDBSymbolTypeFunctionArg &Symbol) override; + void dump(const PDBSymbolTypePointer &Symbol) override; + void dump(const PDBSymbolTypeTypedef &Symbol) override; + void dump(const PDBSymbolTypeUDT &Symbol) override; + +private: + LinePrinter &Printer; +}; +} + +#endif diff --git a/tools/llvm-pdbdump/LLVMBuild.txt b/tools/llvm-pdbdump/LLVMBuild.txt new file mode 100644 index 000000000000..487768935ddd --- /dev/null +++ b/tools/llvm-pdbdump/LLVMBuild.txt @@ -0,0 +1,23 @@ +;===- ./tools/llvm-pdbdump/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-pdbdump +parent = Tools +required_libraries = DebugInfoPDB + diff --git a/tools/llvm-pdbdump/LinePrinter.cpp b/tools/llvm-pdbdump/LinePrinter.cpp new file mode 100644 index 000000000000..6bbc403f5caf --- /dev/null +++ b/tools/llvm-pdbdump/LinePrinter.cpp @@ -0,0 +1,124 @@ +//===- LinePrinter.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "LinePrinter.h" + +#include "llvm-pdbdump.h" + +#include "llvm/Support/Regex.h" + +#include <algorithm> + +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(), + opts::ExcludeSymbols.end()); + SetFilters(CompilandFilters, opts::ExcludeCompilands.begin(), + opts::ExcludeCompilands.end()); +} + +void LinePrinter::Indent() { CurrentIndent += IndentSpaces; } + +void LinePrinter::Unindent() { + CurrentIndent = std::max(0, CurrentIndent - IndentSpaces); +} + +void LinePrinter::NewLine() { + OS << "\n"; + OS.indent(CurrentIndent); +} + +bool LinePrinter::IsTypeExcluded(llvm::StringRef TypeName) { + if (TypeName.empty()) + return false; + + for (auto &Expr : TypeFilters) { + if (Expr.match(TypeName)) + return true; + } + return false; +} + +bool LinePrinter::IsSymbolExcluded(llvm::StringRef SymbolName) { + if (SymbolName.empty()) + return false; + + for (auto &Expr : SymbolFilters) { + if (Expr.match(SymbolName)) + return true; + } + return false; +} + +bool LinePrinter::IsCompilandExcluded(llvm::StringRef CompilandName) { + if (CompilandName.empty()) + return false; + + for (auto &Expr : CompilandFilters) { + if (Expr.match(CompilandName)) + return true; + } + return false; +} + +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); + } +} + +WithColor::~WithColor() { OS.resetColor(); } + +void WithColor::translateColor(PDB_ColorItem C, raw_ostream::Colors &Color, + bool &Bold) const { + switch (C) { + case PDB_ColorItem::Address: + Color = raw_ostream::YELLOW; + Bold = true; + return; + case PDB_ColorItem::Keyword: + Color = raw_ostream::MAGENTA; + Bold = true; + return; + case PDB_ColorItem::Register: + case PDB_ColorItem::Offset: + Color = raw_ostream::YELLOW; + Bold = false; + return; + case PDB_ColorItem::Type: + Color = raw_ostream::CYAN; + Bold = true; + return; + case PDB_ColorItem::Identifier: + Color = raw_ostream::CYAN; + Bold = false; + return; + case PDB_ColorItem::Path: + Color = raw_ostream::CYAN; + Bold = false; + return; + case PDB_ColorItem::SectionHeader: + Color = raw_ostream::RED; + Bold = true; + return; + case PDB_ColorItem::LiteralValue: + Color = raw_ostream::GREEN; + Bold = true; + default: + return; + } +} diff --git a/tools/llvm-pdbdump/LinePrinter.h b/tools/llvm-pdbdump/LinePrinter.h new file mode 100644 index 000000000000..c2a3ab60d90f --- /dev/null +++ b/tools/llvm-pdbdump/LinePrinter.h @@ -0,0 +1,89 @@ +//===- LinePrinter.h ------------------------------------------ *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMPDBDUMP_LINEPRINTER_H +#define LLVM_TOOLS_LLVMPDBDUMP_LINEPRINTER_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Regex.h" + +#include <list> + +namespace llvm { + +class LinePrinter { + friend class WithColor; + +public: + LinePrinter(int Indent, raw_ostream &Stream); + + void Indent(); + void Unindent(); + void NewLine(); + + raw_ostream &getStream() { return OS; } + int getIndentLevel() const { return CurrentIndent; } + + bool IsTypeExcluded(llvm::StringRef TypeName); + bool IsSymbolExcluded(llvm::StringRef SymbolName); + bool IsCompilandExcluded(llvm::StringRef CompilandName); + +private: + template <typename Iter> + void SetFilters(std::list<Regex> &List, Iter Begin, Iter End) { + List.clear(); + for (; Begin != End; ++Begin) + List.push_back(StringRef(*Begin)); + } + + raw_ostream &OS; + int IndentSpaces; + int CurrentIndent; + + std::list<Regex> CompilandFilters; + std::list<Regex> TypeFilters; + std::list<Regex> SymbolFilters; +}; + +template <class T> +inline raw_ostream &operator<<(LinePrinter &Printer, const T &Item) { + Printer.getStream() << Item; + return Printer.getStream(); +} + +enum class PDB_ColorItem { + None, + Address, + Type, + Keyword, + Offset, + Identifier, + Path, + SectionHeader, + LiteralValue, + Register, +}; + +class WithColor { +public: + WithColor(LinePrinter &P, PDB_ColorItem C); + ~WithColor(); + + raw_ostream &get() { return OS; } + +private: + void translateColor(PDB_ColorItem C, raw_ostream::Colors &Color, + bool &Bold) const; + raw_ostream &OS; +}; +} + +#endif diff --git a/tools/llvm-pdbdump/Makefile b/tools/llvm-pdbdump/Makefile new file mode 100644 index 000000000000..18aafc4076fe --- /dev/null +++ b/tools/llvm-pdbdump/Makefile @@ -0,0 +1,17 @@ +##===- tools/llvm-pdbdump/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-pdbdump +LINK_COMPONENTS := DebugInfoPDB Object + +# This tool has no plugins, optimize startup time. +TOOL_NO_EXPORTS := 1 + +include $(LEVEL)/Makefile.common diff --git a/tools/llvm-pdbdump/TypeDumper.cpp b/tools/llvm-pdbdump/TypeDumper.cpp new file mode 100644 index 000000000000..88c0bd65697e --- /dev/null +++ b/tools/llvm-pdbdump/TypeDumper.cpp @@ -0,0 +1,97 @@ +//===- TypeDumper.cpp - PDBSymDumper implementation for types *----- C++ *-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "TypeDumper.h" + +#include "BuiltinDumper.h" +#include "ClassDefinitionDumper.h" +#include "EnumDumper.h" +#include "LinePrinter.h" +#include "llvm-pdbdump.h" +#include "TypedefDumper.h" + +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDBSymbolExe.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" + +using namespace llvm; + +TypeDumper::TypeDumper(LinePrinter &P) : PDBSymDumper(true), Printer(P) {} + +void TypeDumper::start(const PDBSymbolExe &Exe) { + auto Enums = Exe.findAllChildren<PDBSymbolTypeEnum>(); + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Identifier).get() << "Enums"; + Printer << ": (" << Enums->getChildCount() << " items)"; + Printer.Indent(); + while (auto Enum = Enums->getNext()) + Enum->dump(*this); + Printer.Unindent(); + + auto Typedefs = Exe.findAllChildren<PDBSymbolTypeTypedef>(); + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Identifier).get() << "Typedefs"; + Printer << ": (" << Typedefs->getChildCount() << " items)"; + Printer.Indent(); + while (auto Typedef = Typedefs->getNext()) + Typedef->dump(*this); + Printer.Unindent(); + + auto Classes = Exe.findAllChildren<PDBSymbolTypeUDT>(); + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Identifier).get() << "Classes"; + Printer << ": (" << Classes->getChildCount() << " items)"; + Printer.Indent(); + while (auto Class = Classes->getNext()) + Class->dump(*this); + Printer.Unindent(); +} + +void TypeDumper::dump(const PDBSymbolTypeEnum &Symbol) { + if (Symbol.getUnmodifiedTypeId() != 0) + return; + if (Printer.IsTypeExcluded(Symbol.getName())) + return; + // Dump member enums when dumping their class definition. + if (Symbol.isNested()) + return; + + Printer.NewLine(); + EnumDumper Dumper(Printer); + Dumper.start(Symbol); +} + +void TypeDumper::dump(const PDBSymbolTypeTypedef &Symbol) { + if (Printer.IsTypeExcluded(Symbol.getName())) + return; + + Printer.NewLine(); + TypedefDumper Dumper(Printer); + Dumper.start(Symbol); +} + +void TypeDumper::dump(const PDBSymbolTypeUDT &Symbol) { + if (Symbol.getUnmodifiedTypeId() != 0) + return; + if (Printer.IsTypeExcluded(Symbol.getName())) + return; + + Printer.NewLine(); + + if (opts::NoClassDefs) { + WithColor(Printer, PDB_ColorItem::Keyword).get() << "class "; + WithColor(Printer, PDB_ColorItem::Identifier).get() << Symbol.getName(); + } else { + ClassDefinitionDumper Dumper(Printer); + Dumper.start(Symbol); + } +} diff --git a/tools/llvm-pdbdump/TypeDumper.h b/tools/llvm-pdbdump/TypeDumper.h new file mode 100644 index 000000000000..5c0832eccaf9 --- /dev/null +++ b/tools/llvm-pdbdump/TypeDumper.h @@ -0,0 +1,34 @@ +//===- TypeDumper.h - PDBSymDumper implementation for types *- C++ ------*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMPDBDUMP_TYPEDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_TYPEDUMPER_H + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +namespace llvm { + +class LinePrinter; + +class TypeDumper : public PDBSymDumper { +public: + TypeDumper(LinePrinter &P); + + void start(const PDBSymbolExe &Exe); + + void dump(const PDBSymbolTypeEnum &Symbol) override; + void dump(const PDBSymbolTypeTypedef &Symbol) override; + void dump(const PDBSymbolTypeUDT &Symbol) override; + +private: + LinePrinter &Printer; +}; +} + +#endif diff --git a/tools/llvm-pdbdump/TypedefDumper.cpp b/tools/llvm-pdbdump/TypedefDumper.cpp new file mode 100644 index 000000000000..a6b5c53a6b3a --- /dev/null +++ b/tools/llvm-pdbdump/TypedefDumper.cpp @@ -0,0 +1,79 @@ +//===- TypedefDumper.cpp - PDBSymDumper impl for typedefs -------- * C++ *-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "TypedefDumper.h" + +#include "BuiltinDumper.h" +#include "FunctionDumper.h" +#include "LinePrinter.h" +#include "llvm-pdbdump.h" + +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDBExtras.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" + +using namespace llvm; + +TypedefDumper::TypedefDumper(LinePrinter &P) : PDBSymDumper(true), Printer(P) {} + +void TypedefDumper::start(const PDBSymbolTypeTypedef &Symbol) { + WithColor(Printer, PDB_ColorItem::Keyword).get() << "typedef "; + uint32_t TargetId = Symbol.getTypeId(); + if (auto TypeSymbol = Symbol.getSession().getSymbolById(TargetId)) + TypeSymbol->dump(*this); + WithColor(Printer, PDB_ColorItem::Identifier).get() << " " + << Symbol.getName(); +} + +void TypedefDumper::dump(const PDBSymbolTypeArray &Symbol) {} + +void TypedefDumper::dump(const PDBSymbolTypeBuiltin &Symbol) { + BuiltinDumper Dumper(Printer); + Dumper.start(Symbol); +} + +void TypedefDumper::dump(const PDBSymbolTypeEnum &Symbol) { + WithColor(Printer, PDB_ColorItem::Keyword).get() << "enum "; + WithColor(Printer, PDB_ColorItem::Type).get() << " " << Symbol.getName(); +} + +void TypedefDumper::dump(const PDBSymbolTypePointer &Symbol) { + if (Symbol.isConstType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << "const "; + if (Symbol.isVolatileType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << "volatile "; + uint32_t PointeeId = Symbol.getTypeId(); + auto PointeeType = Symbol.getSession().getSymbolById(PointeeId); + if (!PointeeType) + return; + if (auto FuncSig = dyn_cast<PDBSymbolTypeFunctionSig>(PointeeType.get())) { + FunctionDumper::PointerType Pointer = FunctionDumper::PointerType::Pointer; + if (Symbol.isReference()) + Pointer = FunctionDumper::PointerType::Reference; + FunctionDumper NestedDumper(Printer); + NestedDumper.start(*FuncSig, nullptr, Pointer); + } else { + PointeeType->dump(*this); + Printer << ((Symbol.isReference()) ? "&" : "*"); + } +} + +void TypedefDumper::dump(const PDBSymbolTypeFunctionSig &Symbol) { + FunctionDumper Dumper(Printer); + Dumper.start(Symbol, nullptr, FunctionDumper::PointerType::None); +} + +void TypedefDumper::dump(const PDBSymbolTypeUDT &Symbol) { + WithColor(Printer, PDB_ColorItem::Keyword).get() << "class "; + WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName(); +} diff --git a/tools/llvm-pdbdump/TypedefDumper.h b/tools/llvm-pdbdump/TypedefDumper.h new file mode 100644 index 000000000000..8cd578cc6d29 --- /dev/null +++ b/tools/llvm-pdbdump/TypedefDumper.h @@ -0,0 +1,37 @@ +//===- TypedefDumper.h - llvm-pdbdump typedef dumper ---------*- C++ ----*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMPDBDUMP_TYPEDEFDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_TYPEDEFDUMPER_H + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +namespace llvm { + +class LinePrinter; + +class TypedefDumper : public PDBSymDumper { +public: + TypedefDumper(LinePrinter &P); + + void start(const PDBSymbolTypeTypedef &Symbol); + + void dump(const PDBSymbolTypeArray &Symbol) override; + void dump(const PDBSymbolTypeBuiltin &Symbol) override; + void dump(const PDBSymbolTypeEnum &Symbol) override; + void dump(const PDBSymbolTypeFunctionSig &Symbol) override; + void dump(const PDBSymbolTypePointer &Symbol) override; + void dump(const PDBSymbolTypeUDT &Symbol) override; + +private: + LinePrinter &Printer; +}; +} + +#endif diff --git a/tools/llvm-pdbdump/VariableDumper.cpp b/tools/llvm-pdbdump/VariableDumper.cpp new file mode 100644 index 000000000000..e5665b5fcafa --- /dev/null +++ b/tools/llvm-pdbdump/VariableDumper.cpp @@ -0,0 +1,170 @@ +//===- VariableDumper.cpp - -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "VariableDumper.h" + +#include "BuiltinDumper.h" +#include "LinePrinter.h" +#include "llvm-pdbdump.h" +#include "FunctionDumper.h" + +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeArray.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" + +#include "llvm/Support/Format.h" + +using namespace llvm; + +VariableDumper::VariableDumper(LinePrinter &P) + : PDBSymDumper(true), Printer(P) {} + +void VariableDumper::start(const PDBSymbolData &Var) { + if (Var.isCompilerGenerated() && opts::ExcludeCompilerGenerated) + return; + if (Printer.IsSymbolExcluded(Var.getName())) + return; + + auto VarType = Var.getType(); + + switch (auto LocType = Var.getLocationType()) { + case PDB_LocType::Static: + Printer.NewLine(); + Printer << "data ["; + WithColor(Printer, PDB_ColorItem::Address).get() + << format_hex(Var.getVirtualAddress(), 10); + Printer << "] "; + WithColor(Printer, PDB_ColorItem::Keyword).get() << "static "; + dumpSymbolTypeAndName(*VarType, Var.getName()); + break; + case PDB_LocType::Constant: + if (isa<PDBSymbolTypeEnum>(*VarType)) + break; + Printer.NewLine(); + Printer << "data "; + WithColor(Printer, PDB_ColorItem::Keyword).get() << "const "; + dumpSymbolTypeAndName(*VarType, Var.getName()); + Printer << " = "; + WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Var.getValue(); + break; + case PDB_LocType::ThisRel: + Printer.NewLine(); + Printer << "data "; + WithColor(Printer, PDB_ColorItem::Offset).get() + << "+" << format_hex(Var.getOffset(), 4) << " "; + dumpSymbolTypeAndName(*VarType, Var.getName()); + break; + case PDB_LocType::BitField: + Printer.NewLine(); + Printer << "data "; + WithColor(Printer, PDB_ColorItem::Offset).get() + << "+" << format_hex(Var.getOffset(), 4) << " "; + dumpSymbolTypeAndName(*VarType, Var.getName()); + Printer << " : "; + WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Var.getLength(); + break; + default: + Printer.NewLine(); + Printer << "data "; + Printer << "unknown(" << LocType << ") "; + WithColor(Printer, PDB_ColorItem::Identifier).get() << Var.getName(); + break; + } +} + +void VariableDumper::dump(const PDBSymbolTypeBuiltin &Symbol) { + BuiltinDumper Dumper(Printer); + Dumper.start(Symbol); +} + +void VariableDumper::dump(const PDBSymbolTypeEnum &Symbol) { + WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName(); +} + +void VariableDumper::dump(const PDBSymbolTypeFunctionSig &Symbol) {} + +void VariableDumper::dump(const PDBSymbolTypePointer &Symbol) { + auto PointeeType = Symbol.getPointeeType(); + if (!PointeeType) + return; + + if (auto Func = dyn_cast<PDBSymbolFunc>(PointeeType.get())) { + FunctionDumper NestedDumper(Printer); + FunctionDumper::PointerType Pointer = + Symbol.isReference() ? FunctionDumper::PointerType::Reference + : FunctionDumper::PointerType::Pointer; + NestedDumper.start(*Func, Pointer); + } else { + if (Symbol.isConstType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << "const "; + if (Symbol.isVolatileType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << "volatile "; + PointeeType->dump(*this); + Printer << (Symbol.isReference() ? "&" : "*"); + } +} + +void VariableDumper::dump(const PDBSymbolTypeTypedef &Symbol) { + WithColor(Printer, PDB_ColorItem::Keyword).get() << "typedef "; + WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName(); +} + +void VariableDumper::dump(const PDBSymbolTypeUDT &Symbol) { + WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName(); +} + +void VariableDumper::dumpSymbolTypeAndName(const PDBSymbol &Type, + StringRef Name) { + if (auto *ArrayType = dyn_cast<PDBSymbolTypeArray>(&Type)) { + std::string IndexSpec; + raw_string_ostream IndexStream(IndexSpec); + std::unique_ptr<PDBSymbol> ElementType = ArrayType->getElementType(); + while (auto NestedArray = dyn_cast<PDBSymbolTypeArray>(ElementType.get())) { + IndexStream << "["; + IndexStream << NestedArray->getCount(); + IndexStream << "]"; + ElementType = NestedArray->getElementType(); + } + IndexStream << "[" << ArrayType->getCount() << "]"; + ElementType->dump(*this); + WithColor(Printer, PDB_ColorItem::Identifier).get() << " " << Name; + Printer << IndexStream.str(); + } else { + if (!tryDumpFunctionPointer(Type, Name)) { + Type.dump(*this); + WithColor(Printer, PDB_ColorItem::Identifier).get() << " " << Name; + } + } +} + +bool VariableDumper::tryDumpFunctionPointer(const PDBSymbol &Type, + StringRef Name) { + // Function pointers come across as pointers to function signatures. But the + // signature carries no name, so we have to handle this case separately. + if (auto *PointerType = dyn_cast<PDBSymbolTypePointer>(&Type)) { + auto PointeeType = PointerType->getPointeeType(); + if (auto *FunctionSig = + dyn_cast<PDBSymbolTypeFunctionSig>(PointeeType.get())) { + FunctionDumper Dumper(Printer); + FunctionDumper::PointerType PT = FunctionDumper::PointerType::Pointer; + if (PointerType->isReference()) + PT = FunctionDumper::PointerType::Reference; + std::string NameStr(Name.begin(), Name.end()); + Dumper.start(*FunctionSig, NameStr.c_str(), PT); + return true; + } + } + return false; +} diff --git a/tools/llvm-pdbdump/VariableDumper.h b/tools/llvm-pdbdump/VariableDumper.h new file mode 100644 index 000000000000..db8d8ea0e430 --- /dev/null +++ b/tools/llvm-pdbdump/VariableDumper.h @@ -0,0 +1,41 @@ +//===- VariableDumper.h - PDBSymDumper implementation for types -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMPDBDUMP_VARIABLEDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_VARIABLEDUMPER_H + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" +#include "llvm/ADT/StringRef.h" + +namespace llvm { + +class LinePrinter; + +class VariableDumper : public PDBSymDumper { +public: + VariableDumper(LinePrinter &P); + + void start(const PDBSymbolData &Var); + + void dump(const PDBSymbolTypeBuiltin &Symbol) override; + void dump(const PDBSymbolTypeEnum &Symbol) override; + void dump(const PDBSymbolTypeFunctionSig &Symbol) override; + void dump(const PDBSymbolTypePointer &Symbol) override; + void dump(const PDBSymbolTypeTypedef &Symbol) override; + void dump(const PDBSymbolTypeUDT &Symbol) override; + +private: + void dumpSymbolTypeAndName(const PDBSymbol &Type, StringRef Name); + bool tryDumpFunctionPointer(const PDBSymbol &Type, StringRef Name); + + LinePrinter &Printer; +}; +} + +#endif diff --git a/tools/llvm-pdbdump/llvm-pdbdump.cpp b/tools/llvm-pdbdump/llvm-pdbdump.cpp new file mode 100644 index 000000000000..4a4c64b80cc1 --- /dev/null +++ b/tools/llvm-pdbdump/llvm-pdbdump.cpp @@ -0,0 +1,284 @@ +//===- llvm-pdbdump.cpp - Dump debug info from a PDB file -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Dumps debug information present in PDB files. This utility makes use of +// the Microsoft Windows SDK, so will not compile or run on non-Windows +// platforms. +// +//===----------------------------------------------------------------------===// + +#include "llvm-pdbdump.h" +#include "CompilandDumper.h" +#include "ExternalSymbolDumper.h" +#include "FunctionDumper.h" +#include "LinePrinter.h" +#include "TypeDumper.h" +#include "VariableDumper.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Config/config.h" +#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" +#include "llvm/DebugInfo/PDB/IPDBRawSymbol.h" +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDB.h" +#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolExe.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" +#include "llvm/DebugInfo/PDB/PDBSymbolThunk.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/ManagedStatic.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) +#include <Windows.h> +#endif + +using namespace llvm; + +namespace opts { + +enum class PDB_DumpType { ByType, ByObjFile, Both }; + +cl::list<std::string> InputFilenames(cl::Positional, + cl::desc("<input PDB files>"), + cl::OneOrMore); + +cl::OptionCategory TypeCategory("Symbol Type Options"); +cl::OptionCategory FilterCategory("Filtering Options"); +cl::OptionCategory OtherOptions("Other Options"); + +cl::opt<bool> Compilands("compilands", cl::desc("Display compilands"), + cl::cat(TypeCategory)); +cl::opt<bool> Symbols("symbols", cl::desc("Display symbols for each compiland"), + cl::cat(TypeCategory)); +cl::opt<bool> Globals("globals", cl::desc("Dump global symbols"), + cl::cat(TypeCategory)); +cl::opt<bool> Externals("externals", cl::desc("Dump external symbols"), + cl::cat(TypeCategory)); +cl::opt<bool> Types("types", cl::desc("Display types"), cl::cat(TypeCategory)); +cl::opt<bool> + All("all", cl::desc("Implies all other options in 'Symbol Types' category"), + cl::cat(TypeCategory)); + +cl::opt<uint64_t> LoadAddress( + "load-address", + cl::desc("Assume the module is loaded at the specified address"), + cl::cat(OtherOptions)); + +cl::list<std::string> + ExcludeTypes("exclude-types", + cl::desc("Exclude types by regular expression"), + cl::ZeroOrMore, cl::cat(FilterCategory)); +cl::list<std::string> + ExcludeSymbols("exclude-symbols", + cl::desc("Exclude symbols by regular expression"), + cl::ZeroOrMore, cl::cat(FilterCategory)); +cl::list<std::string> + ExcludeCompilands("exclude-compilands", + cl::desc("Exclude compilands by regular expression"), + cl::ZeroOrMore, cl::cat(FilterCategory)); +cl::opt<bool> ExcludeCompilerGenerated( + "no-compiler-generated", + cl::desc("Don't show compiler generated types and symbols"), + cl::cat(FilterCategory)); +cl::opt<bool> + ExcludeSystemLibraries("no-system-libs", + cl::desc("Don't show symbols from system libraries"), + cl::cat(FilterCategory)); +cl::opt<bool> NoClassDefs("no-class-definitions", + cl::desc("Don't display full class definitions"), + cl::cat(FilterCategory)); +cl::opt<bool> NoEnumDefs("no-enum-definitions", + cl::desc("Don't display full enum definitions"), + cl::cat(FilterCategory)); +} + +static void dumpInput(StringRef Path) { + std::unique_ptr<IPDBSession> Session; + PDB_ErrorCode Error = + llvm::loadDataForPDB(PDB_ReaderType::DIA, Path, Session); + switch (Error) { + case PDB_ErrorCode::Success: + break; + case PDB_ErrorCode::NoPdbImpl: + outs() << "Reading PDBs is not supported on this platform.\n"; + return; + case PDB_ErrorCode::InvalidPath: + outs() << "Unable to load PDB at '" << Path + << "'. Check that the file exists and is readable.\n"; + return; + case PDB_ErrorCode::InvalidFileFormat: + outs() << "Unable to load PDB at '" << Path + << "'. The file has an unrecognized format.\n"; + return; + default: + outs() << "Unable to load PDB at '" << Path + << "'. An unknown error occured.\n"; + return; + } + if (opts::LoadAddress) + Session->setLoadAddress(opts::LoadAddress); + + LinePrinter Printer(2, outs()); + + auto GlobalScope(Session->getGlobalScope()); + std::string FileName(GlobalScope->getSymbolsFileName()); + + WithColor(Printer, PDB_ColorItem::None).get() << "Summary for "; + WithColor(Printer, PDB_ColorItem::Path).get() << FileName; + Printer.Indent(); + uint64_t FileSize = 0; + + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Identifier).get() << "Size"; + if (!llvm::sys::fs::file_size(FileName, FileSize)) { + Printer << ": " << FileSize << " bytes"; + } else { + Printer << ": (Unable to obtain file size)"; + } + + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Identifier).get() << "Guid"; + Printer << ": " << GlobalScope->getGuid(); + + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Identifier).get() << "Age"; + Printer << ": " << GlobalScope->getAge(); + + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Identifier).get() << "Attributes"; + Printer << ": "; + if (GlobalScope->hasCTypes()) + outs() << "HasCTypes "; + if (GlobalScope->hasPrivateSymbols()) + outs() << "HasPrivateSymbols "; + Printer.Unindent(); + + if (opts::Compilands) { + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::SectionHeader).get() + << "---COMPILANDS---"; + Printer.Indent(); + auto Compilands = GlobalScope->findAllChildren<PDBSymbolCompiland>(); + CompilandDumper Dumper(Printer); + while (auto Compiland = Compilands->getNext()) + Dumper.start(*Compiland, false); + Printer.Unindent(); + } + + if (opts::Types) { + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---TYPES---"; + Printer.Indent(); + TypeDumper Dumper(Printer); + Dumper.start(*GlobalScope); + Printer.Unindent(); + } + + if (opts::Symbols) { + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---SYMBOLS---"; + Printer.Indent(); + auto Compilands = GlobalScope->findAllChildren<PDBSymbolCompiland>(); + CompilandDumper Dumper(Printer); + while (auto Compiland = Compilands->getNext()) + Dumper.start(*Compiland, true); + Printer.Unindent(); + } + + if (opts::Globals) { + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---GLOBALS---"; + Printer.Indent(); + { + FunctionDumper Dumper(Printer); + auto Functions = GlobalScope->findAllChildren<PDBSymbolFunc>(); + while (auto Function = Functions->getNext()) { + Printer.NewLine(); + Dumper.start(*Function, FunctionDumper::PointerType::None); + } + } + { + auto Vars = GlobalScope->findAllChildren<PDBSymbolData>(); + VariableDumper Dumper(Printer); + while (auto Var = Vars->getNext()) + Dumper.start(*Var); + } + { + auto Thunks = GlobalScope->findAllChildren<PDBSymbolThunk>(); + CompilandDumper Dumper(Printer); + while (auto Thunk = Thunks->getNext()) + Dumper.dump(*Thunk); + } + Printer.Unindent(); + } + if (opts::Externals) { + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---EXTERNALS---"; + Printer.Indent(); + ExternalSymbolDumper Dumper(Printer); + Dumper.start(*GlobalScope); + } + outs().flush(); +} + +int main(int argc_, const char *argv_[]) { + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(); + 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); + if (EC) { + llvm::errs() << "error: couldn't get arguments: " << EC.message() << '\n'; + return 1; + } + + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + + cl::ParseCommandLineOptions(argv.size(), argv.data(), "LLVM PDB Dumper\n"); + if (opts::All) { + opts::Compilands = true; + opts::Symbols = true; + opts::Globals = true; + opts::Types = true; + opts::Externals = true; + } + if (opts::ExcludeCompilerGenerated) { + opts::ExcludeTypes.push_back("__vc_attributes"); + opts::ExcludeCompilands.push_back("* Linker *"); + } + if (opts::ExcludeSystemLibraries) { + opts::ExcludeCompilands.push_back( + "f:\\binaries\\Intermediate\\vctools\\crt_bld"); + } + +#if defined(HAVE_DIA_SDK) + CoInitializeEx(nullptr, COINIT_MULTITHREADED); +#endif + + std::for_each(opts::InputFilenames.begin(), opts::InputFilenames.end(), + dumpInput); + +#if defined(HAVE_DIA_SDK) + CoUninitialize(); +#endif + + return 0; +} diff --git a/tools/llvm-pdbdump/llvm-pdbdump.h b/tools/llvm-pdbdump/llvm-pdbdump.h new file mode 100644 index 000000000000..586a9ea374ed --- /dev/null +++ b/tools/llvm-pdbdump/llvm-pdbdump.h @@ -0,0 +1,32 @@ +//===- llvm-pdbdump.h ----------------------------------------- *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMPDBDUMP_LLVMPDBDUMP_H +#define LLVM_TOOLS_LLVMPDBDUMP_LLVMPDBDUMP_H + +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/raw_ostream.h" + +namespace opts { +extern llvm::cl::opt<bool> Compilands; +extern llvm::cl::opt<bool> Symbols; +extern llvm::cl::opt<bool> Globals; +extern llvm::cl::opt<bool> Types; +extern llvm::cl::opt<bool> All; + +extern llvm::cl::opt<bool> ExcludeCompilerGenerated; + +extern llvm::cl::opt<bool> NoClassDefs; +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; +} + +#endif
\ No newline at end of file diff --git a/tools/llvm-profdata/llvm-profdata.cpp b/tools/llvm-profdata/llvm-profdata.cpp index 25531c776a31..1bfdb181d7a9 100644 --- a/tools/llvm-profdata/llvm-profdata.cpp +++ b/tools/llvm-profdata/llvm-profdata.cpp @@ -22,6 +22,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" @@ -38,7 +39,8 @@ static void exitWithError(const Twine &Message, StringRef Whence = "") { enum ProfileKinds { instr, sample }; -void mergeInstrProfile(cl::list<std::string> Inputs, StringRef OutputFilename) { +static void mergeInstrProfile(const cl::list<std::string> &Inputs, + StringRef OutputFilename) { if (OutputFilename.compare("-") == 0) exitWithError("Cannot write indexed profdata format to stdout."); @@ -64,8 +66,9 @@ void mergeInstrProfile(cl::list<std::string> Inputs, StringRef OutputFilename) { Writer.write(Output); } -void mergeSampleProfile(cl::list<std::string> Inputs, StringRef OutputFilename, - sampleprof::SampleProfileFormat OutputFormat) { +static void mergeSampleProfile(const cl::list<std::string> &Inputs, + StringRef OutputFilename, + sampleprof::SampleProfileFormat OutputFormat) { using namespace sampleprof; auto WriterOrErr = SampleProfileWriter::create(OutputFilename, OutputFormat); if (std::error_code EC = WriterOrErr.getError()) @@ -95,7 +98,7 @@ void mergeSampleProfile(cl::list<std::string> Inputs, StringRef OutputFilename, Writer->write(ProfileMap); } -int merge_main(int argc, const char *argv[]) { +static int merge_main(int argc, const char *argv[]) { cl::list<std::string> Inputs(cl::Positional, cl::Required, cl::OneOrMore, cl::desc("<filenames...>")); @@ -128,9 +131,9 @@ int merge_main(int argc, const char *argv[]) { return 0; } -int showInstrProfile(std::string Filename, bool ShowCounts, - bool ShowAllFunctions, std::string ShowFunction, - raw_fd_ostream &OS) { +static int showInstrProfile(std::string Filename, bool ShowCounts, + bool ShowAllFunctions, std::string ShowFunction, + raw_fd_ostream &OS) { auto ReaderOrErr = InstrProfReader::create(Filename); if (std::error_code EC = ReaderOrErr.getError()) exitWithError(EC.message(), Filename); @@ -181,9 +184,9 @@ int showInstrProfile(std::string Filename, bool ShowCounts, return 0; } -int showSampleProfile(std::string Filename, bool ShowCounts, - bool ShowAllFunctions, std::string ShowFunction, - raw_fd_ostream &OS) { +static int showSampleProfile(std::string Filename, bool ShowCounts, + bool ShowAllFunctions, std::string ShowFunction, + raw_fd_ostream &OS) { using namespace sampleprof; auto ReaderOrErr = SampleProfileReader::create(Filename, getGlobalContext()); if (std::error_code EC = ReaderOrErr.getError()) @@ -199,7 +202,7 @@ int showSampleProfile(std::string Filename, bool ShowCounts, return 0; } -int show_main(int argc, const char *argv[]) { +static int show_main(int argc, const char *argv[]) { cl::opt<std::string> Filename(cl::Positional, cl::Required, cl::desc("<profdata-file>")); diff --git a/tools/llvm-readobj/COFFDumper.cpp b/tools/llvm-readobj/COFFDumper.cpp index 156e39a35fd5..141211134ffd 100644 --- a/tools/llvm-readobj/COFFDumper.cpp +++ b/tools/llvm-readobj/COFFDumper.cpp @@ -71,7 +71,7 @@ private: void printBaseOfDataField(const pe32_header *Hdr); void printBaseOfDataField(const pe32plus_header *Hdr); - void printCodeViewLineTables(const SectionRef &Section); + void printCodeViewDebugInfo(const SectionRef &Section); void printCodeViewSymbolsSubsection(StringRef Subsection, const SectionRef &Section, @@ -469,7 +469,7 @@ void COFFDumper::printBaseOfDataField(const pe32_header *Hdr) { void COFFDumper::printBaseOfDataField(const pe32plus_header *) {} -void COFFDumper::printCodeViewLineTables(const SectionRef &Section) { +void COFFDumper::printCodeViewDebugInfo(const SectionRef &Section) { StringRef Data; if (error(Section.getContents(Data))) return; @@ -477,7 +477,7 @@ void COFFDumper::printCodeViewLineTables(const SectionRef &Section) { SmallVector<StringRef, 10> FunctionNames; StringMap<StringRef> FunctionLineTables; - ListScope D(W, "CodeViewLineTables"); + ListScope D(W, "CodeViewDebugInfo"); { // FIXME: Add more offset correctness checks. DataExtractor DE(Data, true, 4); @@ -503,14 +503,17 @@ void COFFDumper::printCodeViewLineTables(const SectionRef &Section) { return; } - // Print the raw contents to simplify debugging if anything goes wrong - // afterwards. StringRef Contents = Data.substr(Offset, PayloadSize); - W.printBinaryBlock("Contents", Contents); + if (opts::CodeViewSubsectionBytes) { + // Print the raw contents to simplify debugging if anything goes wrong + // afterwards. + W.printBinaryBlock("Contents", Contents); + } switch (SubSectionType) { case COFF::DEBUG_SYMBOL_SUBSECTION: - printCodeViewSymbolsSubsection(Contents, Section, Offset); + if (opts::SectionSymbols) + 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 @@ -695,10 +698,20 @@ void COFFDumper::printCodeViewSymbolsSubsection(StringRef Subsection, InFunctionScope = false; break; } - default: + default: { + if (opts::CodeViewSubsectionBytes) { + ListScope S(W, "Record"); + W.printHex("Size", Size); + W.printHex("Type", Type); + + StringRef Contents = DE.getData().substr(Offset, Size); + W.printBinaryBlock("Contents", Contents); + } + Offset += Size; break; } + } } if (InFunctionScope) @@ -747,8 +760,8 @@ void COFFDumper::printSections() { } } - if (Name == ".debug$S" && opts::CodeViewLineTables) - printCodeViewLineTables(Sec); + if (Name == ".debug$S" && opts::CodeView) + printCodeViewDebugInfo(Sec); if (opts::SectionData && !(Section->Characteristics & COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA)) { @@ -1107,6 +1120,7 @@ static StringRef getBaseRelocTypeName(uint8_t Type) { case COFF::IMAGE_REL_BASED_LOW: return "LOW"; case COFF::IMAGE_REL_BASED_HIGHLOW: return "HIGHLOW"; case COFF::IMAGE_REL_BASED_HIGHADJ: return "HIGHADJ"; + case COFF::IMAGE_REL_BASED_ARM_MOV32T: return "ARM_MOV32(T)"; case COFF::IMAGE_REL_BASED_DIR64: return "DIR64"; default: return "unknown (" + llvm::utostr(Type) + ")"; } diff --git a/tools/llvm-readobj/ELFDumper.cpp b/tools/llvm-readobj/ELFDumper.cpp index d68c78682d23..a20512f20534 100644 --- a/tools/llvm-readobj/ELFDumper.cpp +++ b/tools/llvm-readobj/ELFDumper.cpp @@ -26,6 +26,7 @@ #include "llvm/Support/Compiler.h" #include "llvm/Support/Format.h" #include "llvm/Support/MathExtras.h" +#include "llvm/Support/MipsABIFlags.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; @@ -56,6 +57,7 @@ public: void printAttributes() override; void printMipsPLTGOT() override; + void printMipsABIFlags() override; private: typedef ELFFile<ELFT> ELFO; @@ -170,6 +172,16 @@ findSectionByAddress(const ELFFile<ELFT> *Obj, uint64_t Addr) { return nullptr; } +template <class ELFT> +static const typename ELFFile<ELFT>::Elf_Shdr * +findSectionByName(const ELFFile<ELFT> &Obj, StringRef Name) { + for (const auto &Shdr : Obj.sections()) { + if (Name == errorOrDefault(Obj.getSectionName(&Shdr))) + return &Shdr; + } + return nullptr; +} + static const EnumEntry<unsigned> ElfClass[] = { { "None", ELF::ELFCLASSNONE }, { "32-bit", ELF::ELFCLASS32 }, @@ -207,6 +219,7 @@ static const EnumEntry<unsigned> ElfOSABI[] = { { "NSK", ELF::ELFOSABI_NSK }, { "AROS", ELF::ELFOSABI_AROS }, { "FenixOS", ELF::ELFOSABI_FENIXOS }, + { "CloudABI", ELF::ELFOSABI_CLOUDABI }, { "C6000_ELFABI", ELF::ELFOSABI_C6000_ELFABI }, { "C6000_LINUX" , ELF::ELFOSABI_C6000_LINUX }, { "ARM", ELF::ELFOSABI_ARM }, @@ -372,9 +385,10 @@ static const EnumEntry<unsigned> ElfMachineType[] = { }; static const EnumEntry<unsigned> ElfSymbolBindings[] = { - { "Local", ELF::STB_LOCAL }, - { "Global", ELF::STB_GLOBAL }, - { "Weak", ELF::STB_WEAK } + { "Local", ELF::STB_LOCAL }, + { "Global", ELF::STB_GLOBAL }, + { "Weak", ELF::STB_WEAK }, + { "Unique", ELF::STB_GNU_UNIQUE } }; static const EnumEntry<unsigned> ElfSymbolTypes[] = { @@ -469,6 +483,7 @@ static const char *getElfSegmentType(unsigned Arch, unsigned Type) { LLVM_READOBJ_ENUM_CASE(ELF, PT_MIPS_REGINFO); LLVM_READOBJ_ENUM_CASE(ELF, PT_MIPS_RTPROC); LLVM_READOBJ_ENUM_CASE(ELF, PT_MIPS_OPTIONS); + LLVM_READOBJ_ENUM_CASE(ELF, PT_MIPS_ABIFLAGS); } } @@ -503,10 +518,33 @@ static const EnumEntry<unsigned> ElfHeaderMipsFlags[] = { LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_CPIC), LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ABI2), LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_32BITMODE), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_FP64), LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_NAN2008), LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ABI_O32), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ABI_O64), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ABI_EABI32), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ABI_EABI64), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_3900), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_4010), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_4100), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_4650), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_4120), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_4111), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_SB1), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_OCTEON), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_XLR), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_OCTEON2), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_OCTEON3), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_5400), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_5900), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_5500), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_9000), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_LS2E), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_LS2F), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_LS3A), LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MICROMIPS), LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_ASE_M16), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_ASE_MDMX), LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_1), LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_2), LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_3), @@ -549,7 +587,8 @@ void ELFDumper<ELFT>::printFileHeaders() { W.printHex ("SectionHeaderOffset", Header->e_shoff); if (Header->e_machine == EM_MIPS) W.printFlags("Flags", Header->e_flags, makeArrayRef(ElfHeaderMipsFlags), - unsigned(ELF::EF_MIPS_ARCH)); + unsigned(ELF::EF_MIPS_ARCH), unsigned(ELF::EF_MIPS_ABI), + unsigned(ELF::EF_MIPS_MACH)); else W.printFlags("Flags", Header->e_flags); W.printNumber("HeaderSize", Header->e_ehsize); @@ -739,6 +778,7 @@ static const char *getTypeString(uint64_t Type) { LLVM_READOBJ_TYPE_CASE(FINI_ARRAY); LLVM_READOBJ_TYPE_CASE(FINI_ARRAYSZ); LLVM_READOBJ_TYPE_CASE(FLAGS); + LLVM_READOBJ_TYPE_CASE(FLAGS_1); LLVM_READOBJ_TYPE_CASE(HASH); LLVM_READOBJ_TYPE_CASE(INIT); LLVM_READOBJ_TYPE_CASE(INIT_ARRAY); @@ -780,6 +820,7 @@ static const char *getTypeString(uint64_t Type) { LLVM_READOBJ_TYPE_CASE(MIPS_GOTSYM); LLVM_READOBJ_TYPE_CASE(MIPS_RLD_MAP); LLVM_READOBJ_TYPE_CASE(MIPS_PLTGOT); + LLVM_READOBJ_TYPE_CASE(MIPS_OPTIONS); default: return "unknown"; } } @@ -797,6 +838,34 @@ static const EnumEntry<unsigned> ElfDynamicDTFlags[] = { LLVM_READOBJ_DT_FLAG_ENT(DF, STATIC_TLS) }; +static const EnumEntry<unsigned> ElfDynamicDTFlags1[] = { + LLVM_READOBJ_DT_FLAG_ENT(DF_1, NOW), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, GLOBAL), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, GROUP), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, NODELETE), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, LOADFLTR), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, INITFIRST), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, NOOPEN), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, ORIGIN), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, DIRECT), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, TRANS), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, INTERPOSE), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, NODEFLIB), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, NODUMP), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, CONFALT), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, ENDFILTEE), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, DISPRELDNE), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, NODIRECT), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, IGNMULDEF), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, NOKSYMS), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, NOHDR), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, EDITED), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, NORELOC), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, SYMINTPOSE), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, GLOBAUDIT), + LLVM_READOBJ_DT_FLAG_ENT(DF_1, SINGLETON) +}; + static const EnumEntry<unsigned> ElfDynamicDTMipsFlags[] = { LLVM_READOBJ_DT_FLAG_ENT(RHF, NONE), LLVM_READOBJ_DT_FLAG_ENT(RHF, QUICKSTART), @@ -871,6 +940,7 @@ static void printValue(const ELFFile<ELFT> *O, uint64_t Type, uint64_t Value, case DT_MIPS_GOTSYM: case DT_MIPS_RLD_MAP: case DT_MIPS_PLTGOT: + case DT_MIPS_OPTIONS: OS << format("0x%" PRIX64, Value); break; case DT_RELCOUNT: @@ -909,6 +979,9 @@ static void printValue(const ELFFile<ELFT> *O, uint64_t Type, uint64_t Value, case DT_FLAGS: printFlags(Value, makeArrayRef(ElfDynamicDTFlags), OS); break; + case DT_FLAGS_1: + printFlags(Value, makeArrayRef(ElfDynamicDTFlags1), OS); + break; } } @@ -1240,3 +1313,112 @@ template <class ELFT> void ELFDumper<ELFT>::printMipsPLTGOT() { MipsGOTParser<ELFT>(Obj, W).parseGOT(*GotShdr); } + +static const EnumEntry<unsigned> ElfMipsISAExtType[] = { + {"None", Mips::AFL_EXT_NONE}, + {"Broadcom SB-1", Mips::AFL_EXT_SB1}, + {"Cavium Networks Octeon", Mips::AFL_EXT_OCTEON}, + {"Cavium Networks Octeon2", Mips::AFL_EXT_OCTEON2}, + {"Cavium Networks OcteonP", Mips::AFL_EXT_OCTEONP}, + {"Cavium Networks Octeon3", Mips::AFL_EXT_OCTEON3}, + {"LSI R4010", Mips::AFL_EXT_4010}, + {"Loongson 2E", Mips::AFL_EXT_LOONGSON_2E}, + {"Loongson 2F", Mips::AFL_EXT_LOONGSON_2F}, + {"Loongson 3A", Mips::AFL_EXT_LOONGSON_3A}, + {"MIPS R4650", Mips::AFL_EXT_4650}, + {"MIPS R5900", Mips::AFL_EXT_5900}, + {"MIPS R10000", Mips::AFL_EXT_10000}, + {"NEC VR4100", Mips::AFL_EXT_4100}, + {"NEC VR4111/VR4181", Mips::AFL_EXT_4111}, + {"NEC VR4120", Mips::AFL_EXT_4120}, + {"NEC VR5400", Mips::AFL_EXT_5400}, + {"NEC VR5500", Mips::AFL_EXT_5500}, + {"RMI Xlr", Mips::AFL_EXT_XLR}, + {"Toshiba R3900", Mips::AFL_EXT_3900} +}; + +static const EnumEntry<unsigned> ElfMipsASEFlags[] = { + {"DSP", Mips::AFL_ASE_DSP}, + {"DSPR2", Mips::AFL_ASE_DSPR2}, + {"Enhanced VA Scheme", Mips::AFL_ASE_EVA}, + {"MCU", Mips::AFL_ASE_MCU}, + {"MDMX", Mips::AFL_ASE_MDMX}, + {"MIPS-3D", Mips::AFL_ASE_MIPS3D}, + {"MT", Mips::AFL_ASE_MT}, + {"SmartMIPS", Mips::AFL_ASE_SMARTMIPS}, + {"VZ", Mips::AFL_ASE_VIRT}, + {"MSA", Mips::AFL_ASE_MSA}, + {"MIPS16", Mips::AFL_ASE_MIPS16}, + {"microMIPS", Mips::AFL_ASE_MICROMIPS}, + {"XPA", Mips::AFL_ASE_XPA} +}; + +static const EnumEntry<unsigned> ElfMipsFpABIType[] = { + {"Hard or soft float", Mips::Val_GNU_MIPS_ABI_FP_ANY}, + {"Hard float (double precision)", Mips::Val_GNU_MIPS_ABI_FP_DOUBLE}, + {"Hard float (single precision)", Mips::Val_GNU_MIPS_ABI_FP_SINGLE}, + {"Soft float", Mips::Val_GNU_MIPS_ABI_FP_SOFT}, + {"Hard float (MIPS32r2 64-bit FPU 12 callee-saved)", + Mips::Val_GNU_MIPS_ABI_FP_OLD_64}, + {"Hard float (32-bit CPU, Any FPU)", Mips::Val_GNU_MIPS_ABI_FP_XX}, + {"Hard float (32-bit CPU, 64-bit FPU)", Mips::Val_GNU_MIPS_ABI_FP_64}, + {"Hard float compat (32-bit CPU, 64-bit FPU)", + Mips::Val_GNU_MIPS_ABI_FP_64A} +}; + +static const EnumEntry<unsigned> ElfMipsFlags1[] { + {"ODDSPREG", Mips::AFL_FLAGS1_ODDSPREG}, +}; + +static int getMipsRegisterSize(uint8_t Flag) { + switch (Flag) { + case Mips::AFL_REG_NONE: + return 0; + case Mips::AFL_REG_32: + return 32; + case Mips::AFL_REG_64: + return 64; + case Mips::AFL_REG_128: + return 128; + default: + return -1; + } +} + +template <class ELFT> void ELFDumper<ELFT>::printMipsABIFlags() { + const Elf_Shdr *Shdr = findSectionByName(*Obj, ".MIPS.abiflags"); + if (!Shdr) { + W.startLine() << "There is no .MIPS.abiflags section in the file.\n"; + return; + } + ErrorOr<ArrayRef<uint8_t>> Sec = Obj->getSectionContents(Shdr); + if (!Sec) { + W.startLine() << "The .MIPS.abiflags section is empty.\n"; + return; + } + if (Sec->size() != sizeof(Elf_Mips_ABIFlags<ELFT>)) { + W.startLine() << "The .MIPS.abiflags section has a wrong size.\n"; + return; + } + + auto *Flags = reinterpret_cast<const Elf_Mips_ABIFlags<ELFT> *>(Sec->data()); + + raw_ostream &OS = W.getOStream(); + DictScope GS(W, "MIPS ABI Flags"); + + W.printNumber("Version", Flags->version); + W.startLine() << "ISA: "; + if (Flags->isa_rev <= 1) + OS << format("MIPS%u", Flags->isa_level); + else + OS << format("MIPS%ur%u", Flags->isa_level, Flags->isa_rev); + OS << "\n"; + W.printEnum("ISA Extension", Flags->isa_ext, makeArrayRef(ElfMipsISAExtType)); + W.printFlags("ASEs", Flags->ases, makeArrayRef(ElfMipsASEFlags)); + W.printEnum("FP ABI", Flags->fp_abi, makeArrayRef(ElfMipsFpABIType)); + W.printNumber("GPR size", getMipsRegisterSize(Flags->gpr_size)); + W.printNumber("CPR1 size", getMipsRegisterSize(Flags->cpr1_size)); + W.printNumber("CPR2 size", getMipsRegisterSize(Flags->cpr2_size)); + W.printFlags("Flags 1", Flags->flags1, makeArrayRef(ElfMipsFlags1)); + W.printHex("Flags 2", Flags->flags2); +} diff --git a/tools/llvm-readobj/MachODumper.cpp b/tools/llvm-readobj/MachODumper.cpp index 7e8fdadd855b..40691a222f04 100644 --- a/tools/llvm-readobj/MachODumper.cpp +++ b/tools/llvm-readobj/MachODumper.cpp @@ -203,31 +203,6 @@ static const EnumEntry<uint32_t> MachOHeaderFlags[] = { LLVM_READOBJ_ENUM_ENT(MachO, MH_APP_EXTENSION_SAFE), }; -static const EnumEntry<unsigned> MachOSectionTypes[] = { - { "Regular" , 0x00 }, - { "ZeroFill" , 0x01 }, - { "CStringLiterals" , 0x02 }, - { "4ByteLiterals" , 0x03 }, - { "8ByteLiterals" , 0x04 }, - { "LiteralPointers" , 0x05 }, - { "NonLazySymbolPointers" , 0x06 }, - { "LazySymbolPointers" , 0x07 }, - { "SymbolStubs" , 0x08 }, - { "ModInitFuncs" , 0x09 }, - { "ModTermFuncs" , 0x0A }, - { "Coalesced" , 0x0B }, - { "GBZeroFill" , 0x0C }, - { "Interposing" , 0x0D }, - { "16ByteLiterals" , 0x0E }, - { "DTraceDOF" , 0x0F }, - { "LazyDylibSymbolPoints" , 0x10 }, - { "ThreadLocalRegular" , 0x11 }, - { "ThreadLocalZerofill" , 0x12 }, - { "ThreadLocalVariables" , 0x13 }, - { "ThreadLocalVariablePointers" , 0x14 }, - { "ThreadLocalInitFunctionPointers", 0x15 } -}; - static const EnumEntry<unsigned> MachOSectionAttributes[] = { { "LocReloc" , 1 << 0 /*S_ATTR_LOC_RELOC */ }, { "ExtReloc" , 1 << 1 /*S_ATTR_EXT_RELOC */ }, diff --git a/tools/llvm-readobj/ObjDumper.h b/tools/llvm-readobj/ObjDumper.h index 27e658fc731a..5750d6ffd286 100644 --- a/tools/llvm-readobj/ObjDumper.h +++ b/tools/llvm-readobj/ObjDumper.h @@ -42,6 +42,7 @@ public: // Only implemented for MIPS ELF at this time. virtual void printMipsPLTGOT() { } + virtual void printMipsABIFlags() { } // Only implemented for PE/COFF. virtual void printCOFFImports() { } diff --git a/tools/llvm-readobj/StreamWriter.h b/tools/llvm-readobj/StreamWriter.h index 2fc53eeeec4a..245588ba0600 100644 --- a/tools/llvm-readobj/StreamWriter.h +++ b/tools/llvm-readobj/StreamWriter.h @@ -96,9 +96,10 @@ public: } } - template<typename T, typename TFlag> - void printFlags(StringRef Label, T Value, ArrayRef<EnumEntry<TFlag> > Flags, - TFlag EnumMask = TFlag(0)) { + template <typename T, typename TFlag> + void printFlags(StringRef Label, T Value, ArrayRef<EnumEntry<TFlag>> Flags, + TFlag EnumMask1 = {}, TFlag EnumMask2 = {}, + TFlag EnumMask3 = {}) { typedef EnumEntry<TFlag> FlagEntry; typedef SmallVector<FlagEntry, 10> FlagVector; FlagVector SetFlags; @@ -107,6 +108,13 @@ public: if (Flag.Value == 0) continue; + TFlag EnumMask{}; + if (Flag.Value & EnumMask1) + EnumMask = EnumMask1; + else if (Flag.Value & EnumMask2) + EnumMask = EnumMask2; + else if (Flag.Value & EnumMask3) + EnumMask = EnumMask3; bool IsEnum = (Flag.Value & EnumMask) != 0; if ((!IsEnum && (Value & Flag.Value) == Flag.Value) || (IsEnum && (Value & EnumMask) == Flag.Value)) { diff --git a/tools/llvm-readobj/llvm-readobj.cpp b/tools/llvm-readobj/llvm-readobj.cpp index f95fea8ec3e8..be7bbe94d9ea 100644 --- a/tools/llvm-readobj/llvm-readobj.cpp +++ b/tools/llvm-readobj/llvm-readobj.cpp @@ -25,6 +25,7 @@ #include "StreamWriter.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/MachOUniversal.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" @@ -127,9 +128,14 @@ namespace opts { cl::opt<bool> ExpandRelocs("expand-relocs", cl::desc("Expand each shown relocation to multiple lines")); - // -codeview-linetables - cl::opt<bool> CodeViewLineTables("codeview-linetables", - cl::desc("Display CodeView line table information")); + // -codeview + cl::opt<bool> CodeView("codeview", + cl::desc("Display CodeView debug information")); + + // -codeview-subsection-bytes + cl::opt<bool> CodeViewSubsectionBytes( + "codeview-subsection-bytes", + cl::desc("Dump raw contents of codeview debug sections and records")); // -arm-attributes, -a cl::opt<bool> ARMAttributes("arm-attributes", @@ -142,6 +148,10 @@ namespace opts { MipsPLTGOT("mips-plt-got", cl::desc("Display the MIPS GOT and PLT GOT sections")); + // -mips-abi-flags + cl::opt<bool> MipsABIFlags("mips-abi-flags", + cl::desc("Display the MIPS.abiflags section")); + // -coff-imports cl::opt<bool> COFFImports("coff-imports", cl::desc("Display the PE/COFF import table")); @@ -281,9 +291,12 @@ static void dumpObject(const ObjectFile *Obj) { if (Obj->getArch() == llvm::Triple::arm && Obj->isELF()) if (opts::ARMAttributes) Dumper->printAttributes(); - if (isMipsArch(Obj->getArch()) && Obj->isELF()) + if (isMipsArch(Obj->getArch()) && Obj->isELF()) { if (opts::MipsPLTGOT) Dumper->printMipsPLTGOT(); + if (opts::MipsABIFlags) + Dumper->printMipsABIFlags(); + } if (opts::COFFImports) Dumper->printCOFFImports(); if (opts::COFFExports) @@ -315,6 +328,18 @@ static void dumpArchive(const Archive *Arc) { } } +/// @brief Dumps each object file in \a MachO Universal Binary; +static void dumpMachOUniversalBinary(const MachOUniversalBinary *UBinary) { + for (const MachOUniversalBinary::ObjectForArch &Obj : UBinary->objects()) { + ErrorOr<std::unique_ptr<MachOObjectFile>> ObjOrErr = Obj.getAsObjectFile(); + if (ObjOrErr) + dumpObject(&*ObjOrErr.get()); + else if (ErrorOr<std::unique_ptr<Archive>> AOrErr = Obj.getAsArchive()) + dumpArchive(&*AOrErr.get()); + else + reportError(UBinary->getFileName(), ObjOrErr.getError().message()); + } +} /// @brief Opens \a File and dumps it. static void dumpInput(StringRef File) { @@ -334,6 +359,9 @@ static void dumpInput(StringRef File) { if (Archive *Arc = dyn_cast<Archive>(&Binary)) dumpArchive(Arc); + else if (MachOUniversalBinary *UBinary = + dyn_cast<MachOUniversalBinary>(&Binary)) + dumpMachOUniversalBinary(UBinary); else if (ObjectFile *Obj = dyn_cast<ObjectFile>(&Binary)) dumpObject(Obj); else diff --git a/tools/llvm-readobj/llvm-readobj.h b/tools/llvm-readobj/llvm-readobj.h index 1c334178a24e..74b9a60d34b2 100644 --- a/tools/llvm-readobj/llvm-readobj.h +++ b/tools/llvm-readobj/llvm-readobj.h @@ -36,7 +36,8 @@ namespace opts { extern llvm::cl::opt<bool> DynamicSymbols; extern llvm::cl::opt<bool> UnwindInfo; extern llvm::cl::opt<bool> ExpandRelocs; - extern llvm::cl::opt<bool> CodeViewLineTables; + extern llvm::cl::opt<bool> CodeView; + extern llvm::cl::opt<bool> CodeViewSubsectionBytes; extern llvm::cl::opt<bool> ARMAttributes; extern llvm::cl::opt<bool> MipsPLTGOT; } // namespace opts diff --git a/tools/llvm-rtdyld/CMakeLists.txt b/tools/llvm-rtdyld/CMakeLists.txt index e294760801bb..c1acbe5846bd 100644 --- a/tools/llvm-rtdyld/CMakeLists.txt +++ b/tools/llvm-rtdyld/CMakeLists.txt @@ -1,6 +1,6 @@ set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} - DebugInfo + DebugInfoDWARF ExecutionEngine MC Object diff --git a/tools/llvm-rtdyld/Makefile b/tools/llvm-rtdyld/Makefile index 9de753ef22ad..3e868b985872 100644 --- a/tools/llvm-rtdyld/Makefile +++ b/tools/llvm-rtdyld/Makefile @@ -9,7 +9,7 @@ LEVEL := ../.. TOOLNAME := llvm-rtdyld -LINK_COMPONENTS := all-targets support MC object RuntimeDyld MCJIT debuginfo +LINK_COMPONENTS := all-targets support MC object RuntimeDyld MCJIT DebugInfoDWARF # This tool has no plugins, optimize startup time. TOOL_NO_EXPORTS := 1 diff --git a/tools/llvm-rtdyld/llvm-rtdyld.cpp b/tools/llvm-rtdyld/llvm-rtdyld.cpp index 5b6f301994db..e87f1e2d4c19 100644 --- a/tools/llvm-rtdyld/llvm-rtdyld.cpp +++ b/tools/llvm-rtdyld/llvm-rtdyld.cpp @@ -13,6 +13,8 @@ #include "llvm/ADT/StringMap.h" #include "llvm/DebugInfo/DIContext.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/ExecutionEngine/RTDyldMemoryManager.h" #include "llvm/ExecutionEngine/RuntimeDyld.h" #include "llvm/ExecutionEngine/RuntimeDyldChecker.h" #include "llvm/MC/MCAsmInfo.h" @@ -21,6 +23,7 @@ #include "llvm/MC/MCInstPrinter.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" #include "llvm/Object/MachO.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/DynamicLibrary.h" @@ -45,6 +48,7 @@ InputFileList(cl::Positional, cl::ZeroOrMore, enum ActionType { AC_Execute, AC_PrintLineInfo, + AC_PrintDebugLineInfo, AC_Verify }; @@ -55,6 +59,8 @@ Action(cl::desc("Action to perform:"), "Load, link, and execute the inputs."), clEnumValN(AC_PrintLineInfo, "printline", "Load, link, and print line information for each function."), + clEnumValN(AC_PrintDebugLineInfo, "printdebugline", + "Load, link, and print line information for each function using the debug object"), clEnumValN(AC_Verify, "verify", "Load, link and verify the resulting memory image."), clEnumValEnd)); @@ -186,7 +192,9 @@ static void loadDylibs() { /* *** */ -static int printLineInfoForInput() { +static int printLineInfoForInput(bool LoadObjects, bool UseDebugObj) { + assert(LoadObjects || !UseDebugObj); + // Load any dylibs requested on the command line. loadDylibs(); @@ -196,7 +204,7 @@ static int printLineInfoForInput() { for(unsigned i = 0, e = InputFileList.size(); i != e; ++i) { // Instantiate a dynamic linker. TrivialMemoryManager MemMgr; - RuntimeDyld Dyld(&MemMgr); + RuntimeDyld Dyld(MemMgr, MemMgr); // Load the input memory buffer. @@ -213,24 +221,32 @@ static int printLineInfoForInput() { ObjectFile &Obj = **MaybeObj; - // Load the object file - std::unique_ptr<RuntimeDyld::LoadedObjectInfo> LoadedObjInfo = - Dyld.loadObject(Obj); + OwningBinary<ObjectFile> DebugObj; + std::unique_ptr<RuntimeDyld::LoadedObjectInfo> LoadedObjInfo = nullptr; + ObjectFile *SymbolObj = &Obj; + if (LoadObjects) { + // Load the object file + LoadedObjInfo = + Dyld.loadObject(Obj); - if (Dyld.hasError()) - return Error(Dyld.getErrorString()); + if (Dyld.hasError()) + return Error(Dyld.getErrorString()); - // Resolve all the relocations we can. - Dyld.resolveRelocations(); + // Resolve all the relocations we can. + Dyld.resolveRelocations(); - OwningBinary<ObjectFile> DebugObj = LoadedObjInfo->getObjectForDebug(Obj); + if (UseDebugObj) { + DebugObj = LoadedObjInfo->getObjectForDebug(Obj); + SymbolObj = DebugObj.getBinary(); + } + } std::unique_ptr<DIContext> Context( - DIContext::getDWARFContext(*DebugObj.getBinary())); + new DWARFContextInMemory(*SymbolObj,LoadedObjInfo.get())); // Use symbol info to iterate functions in the object. - for (object::symbol_iterator I = DebugObj.getBinary()->symbol_begin(), - E = DebugObj.getBinary()->symbol_end(); + for (object::symbol_iterator I = SymbolObj->symbol_begin(), + E = SymbolObj->symbol_end(); I != E; ++I) { object::SymbolRef::Type SymType; if (I->getType(SymType)) continue; @@ -242,7 +258,21 @@ static int printLineInfoForInput() { if (I->getAddress(Addr)) continue; if (I->getSize(Size)) continue; - outs() << "Function: " << Name << ", Size = " << Size << "\n"; + // If we're not using the debug object, compute the address of the + // 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()); + I->getSection(Sec); + StringRef SecName; + Sec->getName(SecName); + uint64_t SectionLoadAddress = + LoadedObjInfo->getSectionLoadAddress(SecName); + if (SectionLoadAddress != 0) + Addr += SectionLoadAddress - Sec->getAddress(); + } + + outs() << "Function: " << Name << ", Size = " << Size << ", Addr = " << Addr << "\n"; DILineInfoTable Lines = Context->getLineInfoForAddressRange(Addr, Size); DILineInfoTable::iterator Begin = Lines.begin(); @@ -264,7 +294,12 @@ static int executeInput() { // Instantiate a dynamic linker. TrivialMemoryManager MemMgr; - RuntimeDyld Dyld(&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()) @@ -282,6 +317,7 @@ 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); @@ -298,7 +334,7 @@ static int executeInput() { // FIXME: Error out if there are unresolved relocations. // Get the address of the entry point (_main by default). - void *MainAddress = Dyld.getSymbolAddress(EntryPoint); + void *MainAddress = Dyld.getSymbolLocalAddress(EntryPoint); if (!MainAddress) return Error("no definition for '" + EntryPoint + "'"); @@ -339,7 +375,7 @@ static int checkAllExpressions(RuntimeDyldChecker &Checker) { return 0; } -std::map<void*, uint64_t> +static std::map<void *, uint64_t> applySpecificSectionMappings(RuntimeDyldChecker &Checker) { std::map<void*, uint64_t> SpecificMappings; @@ -397,9 +433,9 @@ applySpecificSectionMappings(RuntimeDyldChecker &Checker) { // Defaults to zero. Set to something big // (e.g. 1 << 32) to stress-test stubs, GOTs, etc. // -void remapSections(const llvm::Triple &TargetTriple, - const TrivialMemoryManager &MemMgr, - RuntimeDyldChecker &Checker) { +static void remapSections(const llvm::Triple &TargetTriple, + const TrivialMemoryManager &MemMgr, + RuntimeDyldChecker &Checker) { // Set up a work list (section addr/size pairs). typedef std::list<std::pair<void*, uint64_t>> WorklistT; @@ -506,18 +542,23 @@ static int linkAndVerify() { std::unique_ptr<MCInstrInfo> MII(TheTarget->createMCInstrInfo()); std::unique_ptr<MCInstPrinter> InstPrinter( - TheTarget->createMCInstPrinter(0, *MAI, *MII, *MRI, *STI)); + TheTarget->createMCInstPrinter(Triple(TripleName), 0, *MAI, *MII, *MRI)); // Load any dylibs requested on the command line. loadDylibs(); // Instantiate a dynamic linker. TrivialMemoryManager MemMgr; - RuntimeDyld Dyld(&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("-"); @@ -536,6 +577,7 @@ 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); @@ -579,8 +621,10 @@ int main(int argc, char **argv) { switch (Action) { case AC_Execute: return executeInput(); + case AC_PrintDebugLineInfo: + return printLineInfoForInput(true,true); case AC_PrintLineInfo: - return printLineInfoForInput(); + return printLineInfoForInput(true,false); case AC_Verify: return linkAndVerify(); } diff --git a/tools/llvm-shlib/CMakeLists.txt b/tools/llvm-shlib/CMakeLists.txt index a896a8a26b52..bc1b658ba726 100644 --- a/tools/llvm-shlib/CMakeLists.txt +++ b/tools/llvm-shlib/CMakeLists.txt @@ -42,7 +42,9 @@ set(SOURCES libllvm.cpp ) -if(NOT DEFINED LLVM_EXPORTED_SYMBOL_FILE) +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.") @@ -53,44 +55,57 @@ if(NOT DEFINED LLVM_EXPORTED_SYMBOL_FILE) set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_BINARY_DIR}/libllvm.exports) - llvm_map_components_to_libnames(LIB_NAMES ${LLVM_DYLIB_COMPONENTS}) - - foreach (lib ${LIB_NAMES}) - - set(LIB_DIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${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}) + 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() - 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} + 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 ${lib} - COMMENT "Generating Export list for ${lib}..." - VERBATIM ) - endforeach () - - add_custom_command(OUTPUT ${LLVM_EXPORTED_SYMBOL_FILE} - COMMAND cat ${LLVM_DYLIB_REQUIRED_EXPORTS} > ${LLVM_EXPORTED_SYMBOL_FILE} - WORKING_DIRECTORY ${LIB_DIR} - DEPENDS ${LLVM_DYLIB_REQUIRED_EXPORTS} - COMMENT "Generating combined export list...") + 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() + add_custom_target(libLLVMExports DEPENDS ${LLVM_EXPORTED_SYMBOL_FILE}) +else() + 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}) +list(REMOVE_DUPLICATES LIB_NAMES) if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") # FIXME: It should be "GNU ld for elf" # GNU ld doesn't resolve symbols in the version script. - list(REMOVE_DUPLICATES LIB_NAMES) set(LIB_NAMES -Wl,--whole-archive ${LIB_NAMES} -Wl,--no-whole-archive) +elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") + set(LIB_NAMES -Wl,-all_load ${LIB_NAMES}) endif() -target_link_libraries(LLVM ${cmake_2_8_12_PRIVATE} ${LIB_NAMES}) +target_link_libraries(LLVM PRIVATE ${LIB_NAMES}) -add_dependencies(LLVM ${LLVM_EXPORTED_SYMBOL_FILE}) +add_dependencies(LLVM libLLVMExports) if (APPLE) set_property(TARGET LLVM APPEND_STRING PROPERTY diff --git a/tools/llvm-size/llvm-size.cpp b/tools/llvm-size/llvm-size.cpp index fc211e3180b4..0e0dd59ce92f 100644 --- a/tools/llvm-size/llvm-size.cpp +++ b/tools/llvm-size/llvm-size.cpp @@ -709,7 +709,7 @@ int main(int argc, char **argv) { ToolName = argv[0]; if (OutputFormatShort.getNumOccurrences()) - OutputFormat = OutputFormatShort; + OutputFormat = static_cast<OutputFormatTy>(OutputFormatShort); if (RadixShort.getNumOccurrences()) Radix = RadixShort; diff --git a/tools/llvm-stress/CMakeLists.txt b/tools/llvm-stress/CMakeLists.txt index 106ced141944..d5c10e13f5b3 100644 --- a/tools/llvm-stress/CMakeLists.txt +++ b/tools/llvm-stress/CMakeLists.txt @@ -7,4 +7,4 @@ set(LLVM_LINK_COMPONENTS add_llvm_tool(llvm-stress llvm-stress.cpp ) -set_target_properties(llvm-stress PROPERTIES ENABLE_EXPORTS 1) +export_executable_symbols(llvm-stress) diff --git a/tools/llvm-stress/llvm-stress.cpp b/tools/llvm-stress/llvm-stress.cpp index 21a79e3294a0..f5e718ba65b6 100644 --- a/tools/llvm-stress/llvm-stress.cpp +++ b/tools/llvm-stress/llvm-stress.cpp @@ -20,7 +20,7 @@ #include "llvm/IR/LegacyPassNameParser.h" #include "llvm/IR/Module.h" #include "llvm/IR/Verifier.h" -#include "llvm/PassManager.h" +#include "llvm/IR/LegacyPassManager.h" #include "llvm/Support/Debug.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/ManagedStatic.h" @@ -711,9 +711,8 @@ int main(int argc, char **argv) { return 1; } - PassManager Passes; + legacy::PassManager Passes; Passes.add(createVerifierPass()); - Passes.add(createDebugInfoVerifierPass()); Passes.add(createPrintModulePass(Out->os())); Passes.run(*M.get()); Out->keep(); diff --git a/tools/llvm-symbolizer/CMakeLists.txt b/tools/llvm-symbolizer/CMakeLists.txt index 9e76248d83d1..5df3b17a065e 100644 --- a/tools/llvm-symbolizer/CMakeLists.txt +++ b/tools/llvm-symbolizer/CMakeLists.txt @@ -4,7 +4,8 @@ # targets as well. Currently, there is no support for such a build strategy. set(LLVM_LINK_COMPONENTS - DebugInfo + DebugInfoDWARF + DebugInfoPDB Object Support ) diff --git a/tools/llvm-symbolizer/LLVMSymbolize.cpp b/tools/llvm-symbolizer/LLVMSymbolize.cpp index 36061d7e684f..afb7cc81c824 100644 --- a/tools/llvm-symbolizer/LLVMSymbolize.cpp +++ b/tools/llvm-symbolizer/LLVMSymbolize.cpp @@ -14,6 +14,9 @@ #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/Support/Casting.h" @@ -26,6 +29,11 @@ #include <sstream> #include <stdlib.h> +#if defined(_MSC_VER) +#include <Windows.h> +#include <DbgHelp.h> +#endif + namespace llvm { namespace symbolize { @@ -163,6 +171,7 @@ DILineInfo ModuleInfo::symbolizeCode( DIInliningInfo ModuleInfo::symbolizeInlinedCode( uint64_t ModuleOffset, const LLVMSymbolizer::Options &Opts) const { DIInliningInfo InlinedContext; + if (DebugInfoContext) { InlinedContext = DebugInfoContext->getInliningInfoForAddress( ModuleOffset, getDILineInfoSpecifier(Opts)); @@ -460,7 +469,20 @@ LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) { Modules.insert(make_pair(ModuleName, (ModuleInfo *)nullptr)); return nullptr; } - DIContext *Context = DIContext::getDWARFContext(*Objects.second); + 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)); @@ -507,7 +529,17 @@ std::string LLVMSymbolizer::DemangleName(const std::string &Name) { free(DemangledName); return Result; #else - return Name; + 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 } diff --git a/tools/llvm-symbolizer/LLVMSymbolize.h b/tools/llvm-symbolizer/LLVMSymbolize.h index ff848fceb242..1c2006fbbe75 100644 --- a/tools/llvm-symbolizer/LLVMSymbolize.h +++ b/tools/llvm-symbolizer/LLVMSymbolize.h @@ -35,19 +35,20 @@ class ModuleInfo; class LLVMSymbolizer { public: struct Options { - bool UseSymbolTable : 1; FunctionNameKind PrintFunctions; + bool UseSymbolTable : 1; bool PrintInlining : 1; bool Demangle : 1; + bool RelativeAddresses : 1; std::string DefaultArch; std::vector<std::string> DsymHints; - Options(bool UseSymbolTable = true, - FunctionNameKind PrintFunctions = FunctionNameKind::LinkageName, - bool PrintInlining = true, bool Demangle = true, + Options(FunctionNameKind PrintFunctions = FunctionNameKind::LinkageName, + bool UseSymbolTable = true, bool PrintInlining = true, + bool Demangle = true, bool RelativeAddresses = false, std::string DefaultArch = "") - : UseSymbolTable(UseSymbolTable), - PrintFunctions(PrintFunctions), PrintInlining(PrintInlining), - Demangle(Demangle), DefaultArch(DefaultArch) {} + : PrintFunctions(PrintFunctions), UseSymbolTable(UseSymbolTable), + PrintInlining(PrintInlining), Demangle(Demangle), + RelativeAddresses(RelativeAddresses), DefaultArch(DefaultArch) {} }; LLVMSymbolizer(const Options &Opts = Options()) : Opts(Opts) {} diff --git a/tools/llvm-symbolizer/Makefile b/tools/llvm-symbolizer/Makefile index 5ac83a58137d..de75befb1c97 100644 --- a/tools/llvm-symbolizer/Makefile +++ b/tools/llvm-symbolizer/Makefile @@ -9,7 +9,7 @@ LEVEL := ../.. TOOLNAME := llvm-symbolizer -LINK_COMPONENTS := DebugInfo Object +LINK_COMPONENTS := DebugInfoDWARF DebugInfoPDB Object # 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 d554022e450c..9c9f3adbf60e 100644 --- a/tools/llvm-symbolizer/llvm-symbolizer.cpp +++ b/tools/llvm-symbolizer/llvm-symbolizer.cpp @@ -17,10 +17,12 @@ #include "LLVMSymbolize.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/COM.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/Path.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" @@ -47,8 +49,13 @@ static cl::opt<FunctionNameKind> ClPrintFunctions( clEnumValEnd)); static cl::opt<bool> -ClPrintInlining("inlining", cl::init(true), - cl::desc("Print all inlined frames for a given address")); + ClUseRelativeAddress("relative-address", cl::init(false), + cl::desc("Interpret addresses as relative addresses"), + cl::ReallyHidden); + +static cl::opt<bool> + ClPrintInlining("inlining", cl::init(true), + cl::desc("Print all inlined frames for a given address")); static cl::opt<bool> ClDemangle("demangle", cl::init(true), cl::desc("Demangle function names")); @@ -122,9 +129,12 @@ int main(int argc, char **argv) { PrettyStackTraceProgram X(argc, argv); llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded); + cl::ParseCommandLineOptions(argc, argv, "llvm-symbolizer\n"); - LLVMSymbolizer::Options Opts(ClUseSymbolTable, ClPrintFunctions, - ClPrintInlining, ClDemangle, ClDefaultArch); + LLVMSymbolizer::Options Opts(ClPrintFunctions, ClUseSymbolTable, + ClPrintInlining, ClDemangle, + ClUseRelativeAddress, ClDefaultArch); for (const auto &hint : ClDsymHint) { if (sys::path::extension(hint) == ".dSYM") { Opts.DsymHints.push_back(hint); @@ -145,5 +155,6 @@ int main(int argc, char **argv) { outs() << Result << "\n"; outs().flush(); } + return 0; } diff --git a/tools/lto/lto.cpp b/tools/lto/lto.cpp index ec0372ea5607..d6ceebed728f 100644 --- a/tools/lto/lto.cpp +++ b/tools/lto/lto.cpp @@ -13,17 +13,23 @@ //===----------------------------------------------------------------------===// #include "llvm-c/lto.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/CodeGen/CommandFlags.h" #include "llvm/IR/LLVMContext.h" #include "llvm/LTO/LTOCodeGenerator.h" #include "llvm/LTO/LTOModule.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Signals.h" #include "llvm/Support/TargetSelect.h" // extra command-line flags needed for LTOCodeGenerator -static cl::opt<bool> -DisableOpt("disable-opt", cl::init(false), - cl::desc("Do not run any optimization passes")); +static cl::opt<char> +OptLevel("O", + cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] " + "(default = '-O2')"), + cl::Prefix, + cl::ZeroOrMore, + cl::init('2')); static cl::opt<bool> DisableInline("disable-inlining", cl::init(false), @@ -51,6 +57,12 @@ static bool parsedOptions = false; // Initialize the configured targets if they have not been initialized. static void lto_initialize() { if (!initialized) { +#ifdef LLVM_ON_WIN32 + // Dialog box on crash disabling doesn't work across DLL boundaries, so do + // it here. + llvm::sys::DisableSystemDialogsOnCrash(); +#endif + InitializeAllTargetInfos(); InitializeAllTargets(); InitializeAllTargetMCs(); @@ -77,6 +89,10 @@ static void lto_add_attrs(lto_code_gen_t cg) { CG->setAttr(attrs.c_str()); } + + if (OptLevel < '0' || OptLevel > '3') + report_fatal_error("Optimization level must be between 0 and 3"); + CG->setOptLevel(OptLevel - '0'); } extern const char* lto_get_version() { @@ -241,6 +257,10 @@ bool lto_codegen_add_module(lto_code_gen_t cg, lto_module_t mod) { return !unwrap(cg)->addModule(unwrap(mod)); } +void lto_codegen_set_module(lto_code_gen_t cg, lto_module_t mod) { + unwrap(cg)->setModule(unwrap(mod)); +} + bool lto_codegen_set_debug_model(lto_code_gen_t cg, lto_debug_model debug) { unwrap(cg)->setDebugInfo(debug); return false; @@ -269,37 +289,57 @@ void lto_codegen_add_must_preserve_symbol(lto_code_gen_t cg, unwrap(cg)->addMustPreserveSymbol(symbol); } -bool lto_codegen_write_merged_modules(lto_code_gen_t cg, const char *path) { +static void maybeParseOptions(lto_code_gen_t cg) { if (!parsedOptions) { unwrap(cg)->parseCodeGenDebugOptions(); lto_add_attrs(cg); parsedOptions = true; } +} + +bool lto_codegen_write_merged_modules(lto_code_gen_t cg, const char *path) { + maybeParseOptions(cg); return !unwrap(cg)->writeMergedModules(path, sLastErrorString); } const void *lto_codegen_compile(lto_code_gen_t cg, size_t *length) { - if (!parsedOptions) { - unwrap(cg)->parseCodeGenDebugOptions(); - lto_add_attrs(cg); - parsedOptions = true; - } - return unwrap(cg)->compile(length, DisableOpt, DisableInline, + maybeParseOptions(cg); + return unwrap(cg)->compile(length, DisableInline, DisableGVNLoadPRE, DisableLTOVectorization, sLastErrorString); } +bool lto_codegen_optimize(lto_code_gen_t cg) { + maybeParseOptions(cg); + return !unwrap(cg)->optimize(DisableInline, + DisableGVNLoadPRE, DisableLTOVectorization, + sLastErrorString); +} + +const void *lto_codegen_compile_optimized(lto_code_gen_t cg, size_t *length) { + maybeParseOptions(cg); + return unwrap(cg)->compileOptimized(length, sLastErrorString); +} + bool lto_codegen_compile_to_file(lto_code_gen_t cg, const char **name) { - if (!parsedOptions) { - unwrap(cg)->parseCodeGenDebugOptions(); - lto_add_attrs(cg); - parsedOptions = true; - } + maybeParseOptions(cg); return !unwrap(cg)->compile_to_file( - name, DisableOpt, DisableInline, DisableGVNLoadPRE, + name, DisableInline, DisableGVNLoadPRE, DisableLTOVectorization, sLastErrorString); } void lto_codegen_debug_options(lto_code_gen_t cg, const char *opt) { unwrap(cg)->setCodeGenDebugOptions(opt); } + +unsigned int lto_api_version() { return LTO_API_VERSION; } + +void lto_codegen_set_should_internalize(lto_code_gen_t cg, + bool ShouldInternalize) { + unwrap(cg)->setShouldInternalize(ShouldInternalize); +} + +void lto_codegen_set_should_embed_uselists(lto_code_gen_t cg, + lto_bool_t ShouldEmbedUselists) { + unwrap(cg)->setShouldEmbedUselists(ShouldEmbedUselists); +} diff --git a/tools/lto/lto.exports b/tools/lto/lto.exports index f6ceaac74d18..9145a6ff4dd4 100644 --- a/tools/lto/lto.exports +++ b/tools/lto/lto.exports @@ -22,8 +22,10 @@ lto_module_is_object_file_for_target lto_module_is_object_file_in_memory lto_module_is_object_file_in_memory_for_target lto_module_dispose +lto_api_version lto_codegen_set_diagnostic_handler lto_codegen_add_module +lto_codegen_set_module lto_codegen_add_must_preserve_symbol lto_codegen_compile lto_codegen_create @@ -37,6 +39,9 @@ lto_codegen_set_assembler_args lto_codegen_set_assembler_path lto_codegen_set_cpu lto_codegen_compile_to_file +lto_codegen_optimize +lto_codegen_compile_optimized +lto_codegen_set_should_internalize LLVMCreateDisasm LLVMCreateDisasmCPU LLVMDisasmDispose diff --git a/tools/obj2yaml/elf2yaml.cpp b/tools/obj2yaml/elf2yaml.cpp index d770ce1151ea..8ce70bc3652a 100644 --- a/tools/obj2yaml/elf2yaml.cpp +++ b/tools/obj2yaml/elf2yaml.cpp @@ -21,8 +21,10 @@ namespace { template <class ELFT> class ELFDumper { + typedef object::Elf_Sym_Impl<ELFT> Elf_Sym; typedef typename object::ELFFile<ELFT>::Elf_Shdr Elf_Shdr; typedef typename object::ELFFile<ELFT>::Elf_Sym_Iter Elf_Sym_Iter; + typedef typename object::ELFFile<ELFT>::Elf_Word Elf_Word; const object::ELFFile<ELFT> &Obj; @@ -38,6 +40,8 @@ class ELFDumper { ErrorOr<ELFYAML::RelocationSection *> dumpRelaSection(const Elf_Shdr *Shdr); ErrorOr<ELFYAML::RawContentSection *> dumpContentSection(const Elf_Shdr *Shdr); + ErrorOr<ELFYAML::Group *> dumpGroup(const Elf_Shdr *Shdr); + ErrorOr<ELFYAML::MipsABIFlags *> dumpMipsABIFlags(const Elf_Shdr *Shdr); public: ELFDumper(const object::ELFFile<ELFT> &O); @@ -86,7 +90,20 @@ ErrorOr<ELFYAML::Object *> ELFDumper<ELFT>::dump() { Y->Sections.push_back(std::unique_ptr<ELFYAML::Section>(S.get())); break; } - // FIXME: Support SHT_GROUP section format. + case ELF::SHT_GROUP: { + ErrorOr<ELFYAML::Group *> G = dumpGroup(&Sec); + if (std::error_code EC = G.getError()) + return EC; + Y->Sections.push_back(std::unique_ptr<ELFYAML::Section>(G.get())); + break; + } + case ELF::SHT_MIPS_ABIFLAGS: { + ErrorOr<ELFYAML::MipsABIFlags *> G = dumpMipsABIFlags(&Sec); + if (std::error_code EC = G.getError()) + return EC; + Y->Sections.push_back(std::unique_ptr<ELFYAML::Section>(G.get())); + break; + } default: { ErrorOr<ELFYAML::RawContentSection *> S = dumpContentSection(&Sec); if (std::error_code EC = S.getError()) @@ -275,6 +292,70 @@ ELFDumper<ELFT>::dumpContentSection(const Elf_Shdr *Shdr) { } template <class ELFT> +ErrorOr<ELFYAML::Group *> ELFDumper<ELFT>::dumpGroup(const Elf_Shdr *Shdr) { + auto S = make_unique<ELFYAML::Group>(); + + 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); + const Elf_Shdr *symtab = Obj.getSection(Shdr->sh_link); + auto sectionContents = Obj.getSectionContents(Shdr); + if (std::error_code ec = sectionContents.getError()) + return ec; + ErrorOr<StringRef> symbolName = Obj.getSymbolName(symtab, symbol); + if (std::error_code EC = symbolName.getError()) + return EC; + S->Info = *symbolName; + const Elf_Word *groupMembers = + reinterpret_cast<const Elf_Word *>(sectionContents->data()); + const long count = (Shdr->sh_size) / sizeof(Elf_Word); + ELFYAML::SectionOrType s; + for (int i = 0; i < count; i++) { + if (groupMembers[i] == llvm::ELF::GRP_COMDAT) { + s.sectionNameOrType = "GRP_COMDAT"; + } else { + const Elf_Shdr *sHdr = Obj.getSection(groupMembers[i]); + ErrorOr<StringRef> sectionName = Obj.getSectionName(sHdr); + if (std::error_code ec = sectionName.getError()) + return ec; + s.sectionNameOrType = *sectionName; + } + S->Members.push_back(s); + } + return S.release(); +} + +template <class ELFT> +ErrorOr<ELFYAML::MipsABIFlags *> +ELFDumper<ELFT>::dumpMipsABIFlags(const Elf_Shdr *Shdr) { + assert(Shdr->sh_type == ELF::SHT_MIPS_ABIFLAGS && + "Section type is not SHT_MIPS_ABIFLAGS"); + auto S = make_unique<ELFYAML::MipsABIFlags>(); + if (std::error_code EC = dumpCommonSection(Shdr, *S)) + return EC; + + ErrorOr<ArrayRef<uint8_t>> ContentOrErr = Obj.getSectionContents(Shdr); + if (std::error_code EC = ContentOrErr.getError()) + return EC; + + auto *Flags = reinterpret_cast<const object::Elf_Mips_ABIFlags<ELFT> *>( + ContentOrErr.get().data()); + S->Version = Flags->version; + S->ISALevel = Flags->isa_level; + S->ISARevision = Flags->isa_rev; + S->GPRSize = Flags->gpr_size; + S->CPR1Size = Flags->cpr1_size; + S->CPR2Size = Flags->cpr2_size; + S->FpABI = Flags->fp_abi; + S->ISAExtension = Flags->isa_ext; + S->ASEs = Flags->ases; + S->Flags1 = Flags->flags1; + S->Flags2 = Flags->flags2; + return S.release(); +} + +template <class ELFT> static std::error_code elf2yaml(raw_ostream &Out, const object::ELFFile<ELFT> &Obj) { ELFDumper<ELFT> Dumper(Obj); diff --git a/tools/opt/BreakpointPrinter.cpp b/tools/opt/BreakpointPrinter.cpp index 3cbc0ae50f14..363a7cd80074 100644 --- a/tools/opt/BreakpointPrinter.cpp +++ b/tools/opt/BreakpointPrinter.cpp @@ -29,18 +29,16 @@ struct BreakpointPrinter : public ModulePass { BreakpointPrinter(raw_ostream &out) : ModulePass(ID), Out(out) {} - void getContextName(DIDescriptor Context, std::string &N) { - if (Context.isNameSpace()) { - DINameSpace NS(Context); - if (!NS.getName().empty()) { - getContextName(NS.getContext(), N); - N = N + NS.getName().str() + "::"; + void getContextName(const DIScope *Context, std::string &N) { + if (auto *NS = dyn_cast<DINamespace>(Context)) { + if (!NS->getName().empty()) { + getContextName(NS->getScope(), N); + N = N + NS->getName().str() + "::"; } - } else if (Context.isType()) { - DIType TY(Context); - if (!TY.getName().empty()) { - getContextName(TY.getContext().resolve(TypeIdentifierMap), N); - N = N + TY.getName().str() + "::"; + } else if (auto *TY = dyn_cast<DIType>(Context)) { + if (!TY->getName().empty()) { + getContextName(TY->getScope().resolve(TypeIdentifierMap), N); + N = N + TY->getName().str() + "::"; } } } @@ -55,13 +53,11 @@ struct BreakpointPrinter : public ModulePass { if (NamedMDNode *NMD = M.getNamedMetadata("llvm.dbg.sp")) for (unsigned i = 0, e = NMD->getNumOperands(); i != e; ++i) { std::string Name; - DISubprogram SP(NMD->getOperand(i)); - assert((!SP || SP.isSubprogram()) && - "A MDNode in llvm.dbg.sp should be null or a DISubprogram."); + auto *SP = cast_or_null<DISubprogram>(NMD->getOperand(i)); if (!SP) continue; - getContextName(SP.getContext().resolve(TypeIdentifierMap), Name); - Name = Name + SP.getDisplayName().str(); + getContextName(SP->getScope().resolve(TypeIdentifierMap), Name); + Name = Name + SP->getDisplayName().str(); if (!Name.empty() && Processed.insert(Name).second) { Out << Name << "\n"; } diff --git a/tools/opt/CMakeLists.txt b/tools/opt/CMakeLists.txt index 1d3f08db7d29..5f825220cc88 100644 --- a/tools/opt/CMakeLists.txt +++ b/tools/opt/CMakeLists.txt @@ -16,6 +16,7 @@ set(LLVM_LINK_COMPONENTS Target TransformUtils Vectorize + Passes ) # Support plugins. @@ -26,12 +27,11 @@ add_llvm_tool(opt BreakpointPrinter.cpp GraphPrinters.cpp NewPMDriver.cpp - Passes.cpp PassPrinters.cpp PrintSCC.cpp opt.cpp ) -set_target_properties(opt PROPERTIES ENABLE_EXPORTS 1) +export_executable_symbols(opt) if(WITH_POLLY AND LINK_POLLY_INTO_TOOLS) target_link_libraries(opt Polly) diff --git a/tools/opt/LLVMBuild.txt b/tools/opt/LLVMBuild.txt index b3589f854ff1..b162ab6cfe9b 100644 --- a/tools/opt/LLVMBuild.txt +++ b/tools/opt/LLVMBuild.txt @@ -19,4 +19,4 @@ type = Tool name = opt parent = Tools -required_libraries = AsmParser BitReader BitWriter CodeGen IRReader IPO Instrumentation Scalar ObjCARC all-targets +required_libraries = AsmParser BitReader BitWriter CodeGen IRReader IPO Instrumentation Scalar ObjCARC Passes all-targets diff --git a/tools/opt/Makefile b/tools/opt/Makefile index cfa9c31cb958..2422eb4e4056 100644 --- a/tools/opt/Makefile +++ b/tools/opt/Makefile @@ -9,7 +9,7 @@ LEVEL := ../.. TOOLNAME := opt -LINK_COMPONENTS := bitreader bitwriter asmparser irreader instrumentation scalaropts objcarcopts ipo vectorize all-targets codegen +LINK_COMPONENTS := bitreader bitwriter asmparser irreader instrumentation scalaropts objcarcopts ipo vectorize all-targets codegen passes # Support plugins. NO_DEAD_STRIP := 1 diff --git a/tools/opt/NewPMDriver.cpp b/tools/opt/NewPMDriver.cpp index f215c77459a8..3030d65743af 100644 --- a/tools/opt/NewPMDriver.cpp +++ b/tools/opt/NewPMDriver.cpp @@ -14,7 +14,6 @@ //===----------------------------------------------------------------------===// #include "NewPMDriver.h" -#include "Passes.h" #include "llvm/ADT/StringRef.h" #include "llvm/Analysis/CGSCCPassManager.h" #include "llvm/Bitcode/BitcodeWriterPass.h" @@ -24,9 +23,11 @@ #include "llvm/IR/Module.h" #include "llvm/IR/PassManager.h" #include "llvm/IR/Verifier.h" +#include "llvm/Passes/PassBuilder.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ToolOutputFile.h" +#include "llvm/Target/TargetMachine.h" using namespace llvm; using namespace opt_tool; @@ -36,16 +37,21 @@ static cl::opt<bool> cl::desc("Print pass management debugging information")); bool llvm::runPassPipeline(StringRef Arg0, LLVMContext &Context, Module &M, - tool_output_file *Out, StringRef PassPipeline, - OutputKind OK, VerifierKind VK) { + TargetMachine *TM, tool_output_file *Out, + StringRef PassPipeline, OutputKind OK, + VerifierKind VK, + bool ShouldPreserveAssemblyUseListOrder, + bool ShouldPreserveBitcodeUseListOrder) { + PassBuilder PB(TM); + FunctionAnalysisManager FAM(DebugPM); CGSCCAnalysisManager CGAM(DebugPM); ModuleAnalysisManager MAM(DebugPM); // Register all the basic analyses with the managers. - registerModuleAnalyses(MAM); - registerCGSCCAnalyses(CGAM); - registerFunctionAnalyses(FAM); + PB.registerModuleAnalyses(MAM); + PB.registerCGSCCAnalyses(CGAM); + PB.registerFunctionAnalyses(FAM); // Cross register the analysis managers through their proxies. MAM.registerPass(FunctionAnalysisManagerModuleProxy(FAM)); @@ -59,7 +65,8 @@ bool llvm::runPassPipeline(StringRef Arg0, LLVMContext &Context, Module &M, if (VK > VK_NoVerifier) MPM.addPass(VerifierPass()); - if (!parsePassPipeline(MPM, PassPipeline, VK == VK_VerifyEachPass, DebugPM)) { + if (!PB.parsePassPipeline(MPM, PassPipeline, VK == VK_VerifyEachPass, + DebugPM)) { errs() << Arg0 << ": unable to parse pass pipeline description.\n"; return false; } @@ -72,10 +79,12 @@ bool llvm::runPassPipeline(StringRef Arg0, LLVMContext &Context, Module &M, case OK_NoOutput: break; // No output pass needed. case OK_OutputAssembly: - MPM.addPass(PrintModulePass(Out->os())); + MPM.addPass( + PrintModulePass(Out->os(), "", ShouldPreserveAssemblyUseListOrder)); break; case OK_OutputBitcode: - MPM.addPass(BitcodeWriterPass(Out->os())); + MPM.addPass( + BitcodeWriterPass(Out->os(), ShouldPreserveBitcodeUseListOrder)); break; } diff --git a/tools/opt/NewPMDriver.h b/tools/opt/NewPMDriver.h index f977baca7f83..349a7b1267f8 100644 --- a/tools/opt/NewPMDriver.h +++ b/tools/opt/NewPMDriver.h @@ -26,6 +26,7 @@ namespace llvm { class LLVMContext; class Module; +class TargetMachine; class tool_output_file; namespace opt_tool { @@ -48,8 +49,11 @@ enum VerifierKind { /// file. It's interface is consequentially somewhat ad-hoc, but will go away /// when the transition finishes. bool runPassPipeline(StringRef Arg0, LLVMContext &Context, Module &M, - tool_output_file *Out, StringRef PassPipeline, - opt_tool::OutputKind OK, opt_tool::VerifierKind VK); + TargetMachine *TM, tool_output_file *Out, + StringRef PassPipeline, opt_tool::OutputKind OK, + opt_tool::VerifierKind VK, + bool ShouldPreserveAssemblyUseListOrder, + bool ShouldPreserveBitcodeUseListOrder); } #endif diff --git a/tools/opt/PassRegistry.def b/tools/opt/PassRegistry.def deleted file mode 100644 index c98de60b5706..000000000000 --- a/tools/opt/PassRegistry.def +++ /dev/null @@ -1,65 +0,0 @@ -//===- PassRegistry.def - Registry of passes --------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is used as the registry of passes that are part of the core LLVM -// libraries. This file describes both transformation passes and analyses -// Analyses are registered while transformation passes have names registered -// that can be used when providing a textual pass pipeline. -// -//===----------------------------------------------------------------------===// - -// NOTE: NO INCLUDE GUARD DESIRED! - -#ifndef MODULE_ANALYSIS -#define MODULE_ANALYSIS(NAME, CREATE_PASS) -#endif -MODULE_ANALYSIS("lcg", LazyCallGraphAnalysis()) -MODULE_ANALYSIS("no-op-module", NoOpModuleAnalysis()) -#undef MODULE_ANALYSIS - -#ifndef MODULE_PASS -#define MODULE_PASS(NAME, CREATE_PASS) -#endif -MODULE_PASS("invalidate<all>", InvalidateAllAnalysesPass()) -MODULE_PASS("no-op-module", NoOpModulePass()) -MODULE_PASS("print", PrintModulePass(dbgs())) -MODULE_PASS("print-cg", LazyCallGraphPrinterPass(dbgs())) -MODULE_PASS("verify", VerifierPass()) -#undef MODULE_PASS - -#ifndef CGSCC_ANALYSIS -#define CGSCC_ANALYSIS(NAME, CREATE_PASS) -#endif -CGSCC_ANALYSIS("no-op-cgscc", NoOpCGSCCAnalysis()) -#undef CGSCC_ANALYSIS - -#ifndef CGSCC_PASS -#define CGSCC_PASS(NAME, CREATE_PASS) -#endif -CGSCC_PASS("invalidate<all>", InvalidateAllAnalysesPass()) -CGSCC_PASS("no-op-cgscc", NoOpCGSCCPass()) -#undef CGSCC_PASS - -#ifndef FUNCTION_ANALYSIS -#define FUNCTION_ANALYSIS(NAME, CREATE_PASS) -#endif -FUNCTION_ANALYSIS("domtree", DominatorTreeAnalysis()) -FUNCTION_ANALYSIS("no-op-function", NoOpFunctionAnalysis()) -#undef FUNCTION_ANALYSIS - -#ifndef FUNCTION_PASS -#define FUNCTION_PASS(NAME, CREATE_PASS) -#endif -FUNCTION_PASS("invalidate<all>", InvalidateAllAnalysesPass()) -FUNCTION_PASS("no-op-function", NoOpFunctionPass()) -FUNCTION_PASS("print", PrintFunctionPass(dbgs())) -FUNCTION_PASS("print<domtree>", DominatorTreePrinterPass(dbgs())) -FUNCTION_PASS("verify", VerifierPass()) -FUNCTION_PASS("verify<domtree>", DominatorTreeVerifierPass()) -#undef FUNCTION_PASS diff --git a/tools/opt/Passes.cpp b/tools/opt/Passes.cpp deleted file mode 100644 index 518112a1c88c..000000000000 --- a/tools/opt/Passes.cpp +++ /dev/null @@ -1,397 +0,0 @@ -//===- Passes.cpp - Parsing, selection, and running of passes -------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -/// \file -/// -/// This file provides the infrastructure to parse and build a custom pass -/// manager based on a commandline flag. It also provides helpers to aid in -/// analyzing, debugging, and testing pass structures. -/// -//===----------------------------------------------------------------------===// - -#include "Passes.h" -#include "llvm/Analysis/CGSCCPassManager.h" -#include "llvm/Analysis/LazyCallGraph.h" -#include "llvm/IR/Dominators.h" -#include "llvm/IR/IRPrintingPasses.h" -#include "llvm/IR/PassManager.h" -#include "llvm/IR/Verifier.h" -#include "llvm/Support/Debug.h" - -using namespace llvm; - -namespace { - -/// \brief No-op module pass which does nothing. -struct NoOpModulePass { - PreservedAnalyses run(Module &M) { return PreservedAnalyses::all(); } - static StringRef name() { return "NoOpModulePass"; } -}; - -/// \brief No-op module analysis. -struct NoOpModuleAnalysis { - struct Result {}; - Result run(Module &) { return Result(); } - static StringRef name() { return "NoOpModuleAnalysis"; } - static void *ID() { return (void *)&PassID; } -private: - static char PassID; -}; - -char NoOpModuleAnalysis::PassID; - -/// \brief No-op CGSCC pass which does nothing. -struct NoOpCGSCCPass { - PreservedAnalyses run(LazyCallGraph::SCC &C) { - return PreservedAnalyses::all(); - } - static StringRef name() { return "NoOpCGSCCPass"; } -}; - -/// \brief No-op CGSCC analysis. -struct NoOpCGSCCAnalysis { - struct Result {}; - Result run(LazyCallGraph::SCC &) { return Result(); } - static StringRef name() { return "NoOpCGSCCAnalysis"; } - static void *ID() { return (void *)&PassID; } -private: - static char PassID; -}; - -char NoOpCGSCCAnalysis::PassID; - -/// \brief No-op function pass which does nothing. -struct NoOpFunctionPass { - PreservedAnalyses run(Function &F) { return PreservedAnalyses::all(); } - static StringRef name() { return "NoOpFunctionPass"; } -}; - -/// \brief No-op function analysis. -struct NoOpFunctionAnalysis { - struct Result {}; - Result run(Function &) { return Result(); } - static StringRef name() { return "NoOpFunctionAnalysis"; } - static void *ID() { return (void *)&PassID; } -private: - static char PassID; -}; - -char NoOpFunctionAnalysis::PassID; - -} // End anonymous namespace. - -void llvm::registerModuleAnalyses(ModuleAnalysisManager &MAM) { -#define MODULE_ANALYSIS(NAME, CREATE_PASS) \ - MAM.registerPass(CREATE_PASS); -#include "PassRegistry.def" -} - -void llvm::registerCGSCCAnalyses(CGSCCAnalysisManager &CGAM) { -#define CGSCC_ANALYSIS(NAME, CREATE_PASS) \ - CGAM.registerPass(CREATE_PASS); -#include "PassRegistry.def" -} - -void llvm::registerFunctionAnalyses(FunctionAnalysisManager &FAM) { -#define FUNCTION_ANALYSIS(NAME, CREATE_PASS) \ - FAM.registerPass(CREATE_PASS); -#include "PassRegistry.def" -} - -#ifndef NDEBUG -static bool isModulePassName(StringRef Name) { -#define MODULE_PASS(NAME, CREATE_PASS) if (Name == NAME) return true; -#define MODULE_ANALYSIS(NAME, CREATE_PASS) \ - if (Name == "require<" NAME ">" || Name == "invalidate<" NAME ">") \ - return true; -#include "PassRegistry.def" - - return false; -} -#endif - -static bool isCGSCCPassName(StringRef Name) { -#define CGSCC_PASS(NAME, CREATE_PASS) if (Name == NAME) return true; -#define CGSCC_ANALYSIS(NAME, CREATE_PASS) \ - if (Name == "require<" NAME ">" || Name == "invalidate<" NAME ">") \ - return true; -#include "PassRegistry.def" - - return false; -} - -static bool isFunctionPassName(StringRef Name) { -#define FUNCTION_PASS(NAME, CREATE_PASS) if (Name == NAME) return true; -#define FUNCTION_ANALYSIS(NAME, CREATE_PASS) \ - if (Name == "require<" NAME ">" || Name == "invalidate<" NAME ">") \ - return true; -#include "PassRegistry.def" - - return false; -} - -static bool parseModulePassName(ModulePassManager &MPM, StringRef Name) { -#define MODULE_PASS(NAME, CREATE_PASS) \ - if (Name == NAME) { \ - MPM.addPass(CREATE_PASS); \ - return true; \ - } -#define MODULE_ANALYSIS(NAME, CREATE_PASS) \ - if (Name == "require<" NAME ">") { \ - MPM.addPass(RequireAnalysisPass<decltype(CREATE_PASS)>()); \ - return true; \ - } \ - if (Name == "invalidate<" NAME ">") { \ - MPM.addPass(InvalidateAnalysisPass<decltype(CREATE_PASS)>()); \ - return true; \ - } -#include "PassRegistry.def" - - return false; -} - -static bool parseCGSCCPassName(CGSCCPassManager &CGPM, StringRef Name) { -#define CGSCC_PASS(NAME, CREATE_PASS) \ - if (Name == NAME) { \ - CGPM.addPass(CREATE_PASS); \ - return true; \ - } -#define CGSCC_ANALYSIS(NAME, CREATE_PASS) \ - if (Name == "require<" NAME ">") { \ - CGPM.addPass(RequireAnalysisPass<decltype(CREATE_PASS)>()); \ - return true; \ - } \ - if (Name == "invalidate<" NAME ">") { \ - CGPM.addPass(InvalidateAnalysisPass<decltype(CREATE_PASS)>()); \ - return true; \ - } -#include "PassRegistry.def" - - return false; -} - -static bool parseFunctionPassName(FunctionPassManager &FPM, StringRef Name) { -#define FUNCTION_PASS(NAME, CREATE_PASS) \ - if (Name == NAME) { \ - FPM.addPass(CREATE_PASS); \ - return true; \ - } -#define FUNCTION_ANALYSIS(NAME, CREATE_PASS) \ - if (Name == "require<" NAME ">") { \ - FPM.addPass(RequireAnalysisPass<decltype(CREATE_PASS)>()); \ - return true; \ - } \ - if (Name == "invalidate<" NAME ">") { \ - FPM.addPass(InvalidateAnalysisPass<decltype(CREATE_PASS)>()); \ - return true; \ - } -#include "PassRegistry.def" - - return false; -} - -static bool parseFunctionPassPipeline(FunctionPassManager &FPM, - StringRef &PipelineText, - bool VerifyEachPass, bool DebugLogging) { - for (;;) { - // Parse nested pass managers by recursing. - if (PipelineText.startswith("function(")) { - FunctionPassManager NestedFPM(DebugLogging); - - // Parse the inner pipeline inte the nested manager. - PipelineText = PipelineText.substr(strlen("function(")); - if (!parseFunctionPassPipeline(NestedFPM, PipelineText, VerifyEachPass, - DebugLogging) || - PipelineText.empty()) - return false; - assert(PipelineText[0] == ')'); - PipelineText = PipelineText.substr(1); - - // Add the nested pass manager with the appropriate adaptor. - FPM.addPass(std::move(NestedFPM)); - } else { - // Otherwise try to parse a pass name. - size_t End = PipelineText.find_first_of(",)"); - if (!parseFunctionPassName(FPM, PipelineText.substr(0, End))) - return false; - if (VerifyEachPass) - FPM.addPass(VerifierPass()); - - PipelineText = PipelineText.substr(End); - } - - if (PipelineText.empty() || PipelineText[0] == ')') - return true; - - assert(PipelineText[0] == ','); - PipelineText = PipelineText.substr(1); - } -} - -static bool parseCGSCCPassPipeline(CGSCCPassManager &CGPM, - StringRef &PipelineText, bool VerifyEachPass, - bool DebugLogging) { - for (;;) { - // Parse nested pass managers by recursing. - if (PipelineText.startswith("cgscc(")) { - CGSCCPassManager NestedCGPM(DebugLogging); - - // Parse the inner pipeline into the nested manager. - PipelineText = PipelineText.substr(strlen("cgscc(")); - if (!parseCGSCCPassPipeline(NestedCGPM, PipelineText, VerifyEachPass, - DebugLogging) || - PipelineText.empty()) - return false; - assert(PipelineText[0] == ')'); - PipelineText = PipelineText.substr(1); - - // Add the nested pass manager with the appropriate adaptor. - CGPM.addPass(std::move(NestedCGPM)); - } else if (PipelineText.startswith("function(")) { - FunctionPassManager NestedFPM(DebugLogging); - - // Parse the inner pipeline inte the nested manager. - PipelineText = PipelineText.substr(strlen("function(")); - if (!parseFunctionPassPipeline(NestedFPM, PipelineText, VerifyEachPass, - DebugLogging) || - PipelineText.empty()) - return false; - assert(PipelineText[0] == ')'); - PipelineText = PipelineText.substr(1); - - // Add the nested pass manager with the appropriate adaptor. - CGPM.addPass(createCGSCCToFunctionPassAdaptor(std::move(NestedFPM))); - } else { - // Otherwise try to parse a pass name. - size_t End = PipelineText.find_first_of(",)"); - if (!parseCGSCCPassName(CGPM, PipelineText.substr(0, End))) - return false; - // FIXME: No verifier support for CGSCC passes! - - PipelineText = PipelineText.substr(End); - } - - if (PipelineText.empty() || PipelineText[0] == ')') - return true; - - assert(PipelineText[0] == ','); - PipelineText = PipelineText.substr(1); - } -} - -static bool parseModulePassPipeline(ModulePassManager &MPM, - StringRef &PipelineText, - bool VerifyEachPass, bool DebugLogging) { - for (;;) { - // Parse nested pass managers by recursing. - if (PipelineText.startswith("module(")) { - ModulePassManager NestedMPM(DebugLogging); - - // Parse the inner pipeline into the nested manager. - PipelineText = PipelineText.substr(strlen("module(")); - if (!parseModulePassPipeline(NestedMPM, PipelineText, VerifyEachPass, - DebugLogging) || - PipelineText.empty()) - return false; - assert(PipelineText[0] == ')'); - PipelineText = PipelineText.substr(1); - - // Now add the nested manager as a module pass. - MPM.addPass(std::move(NestedMPM)); - } else if (PipelineText.startswith("cgscc(")) { - CGSCCPassManager NestedCGPM(DebugLogging); - - // Parse the inner pipeline inte the nested manager. - PipelineText = PipelineText.substr(strlen("cgscc(")); - if (!parseCGSCCPassPipeline(NestedCGPM, PipelineText, VerifyEachPass, - DebugLogging) || - PipelineText.empty()) - return false; - assert(PipelineText[0] == ')'); - PipelineText = PipelineText.substr(1); - - // Add the nested pass manager with the appropriate adaptor. - MPM.addPass( - createModuleToPostOrderCGSCCPassAdaptor(std::move(NestedCGPM))); - } else if (PipelineText.startswith("function(")) { - FunctionPassManager NestedFPM(DebugLogging); - - // Parse the inner pipeline inte the nested manager. - PipelineText = PipelineText.substr(strlen("function(")); - if (!parseFunctionPassPipeline(NestedFPM, PipelineText, VerifyEachPass, - DebugLogging) || - PipelineText.empty()) - return false; - assert(PipelineText[0] == ')'); - PipelineText = PipelineText.substr(1); - - // Add the nested pass manager with the appropriate adaptor. - MPM.addPass(createModuleToFunctionPassAdaptor(std::move(NestedFPM))); - } else { - // Otherwise try to parse a pass name. - size_t End = PipelineText.find_first_of(",)"); - if (!parseModulePassName(MPM, PipelineText.substr(0, End))) - return false; - if (VerifyEachPass) - MPM.addPass(VerifierPass()); - - PipelineText = PipelineText.substr(End); - } - - if (PipelineText.empty() || PipelineText[0] == ')') - return true; - - assert(PipelineText[0] == ','); - PipelineText = PipelineText.substr(1); - } -} - -// Primary pass pipeline description parsing routine. -// FIXME: Should this routine accept a TargetMachine or require the caller to -// pre-populate the analysis managers with target-specific stuff? -bool llvm::parsePassPipeline(ModulePassManager &MPM, StringRef PipelineText, - bool VerifyEachPass, bool DebugLogging) { - // By default, try to parse the pipeline as-if it were within an implicit - // 'module(...)' pass pipeline. If this will parse at all, it needs to - // consume the entire string. - if (parseModulePassPipeline(MPM, PipelineText, VerifyEachPass, DebugLogging)) - return PipelineText.empty(); - - // This isn't parsable as a module pipeline, look for the end of a pass name - // and directly drop down to that layer. - StringRef FirstName = - PipelineText.substr(0, PipelineText.find_first_of(",)")); - assert(!isModulePassName(FirstName) && - "Already handled all module pipeline options."); - - // If this looks like a CGSCC pass, parse the whole thing as a CGSCC - // pipeline. - if (isCGSCCPassName(FirstName)) { - CGSCCPassManager CGPM(DebugLogging); - if (!parseCGSCCPassPipeline(CGPM, PipelineText, VerifyEachPass, - DebugLogging) || - !PipelineText.empty()) - return false; - MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM))); - return true; - } - - // Similarly, if this looks like a Function pass, parse the whole thing as - // a Function pipelien. - if (isFunctionPassName(FirstName)) { - FunctionPassManager FPM(DebugLogging); - if (!parseFunctionPassPipeline(FPM, PipelineText, VerifyEachPass, - DebugLogging) || - !PipelineText.empty()) - return false; - MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); - return true; - } - - return false; -} diff --git a/tools/opt/Passes.h b/tools/opt/Passes.h deleted file mode 100644 index 623da9ff4b4a..000000000000 --- a/tools/opt/Passes.h +++ /dev/null @@ -1,78 +0,0 @@ -//===- Passes.h - Parsing, selection, and running of passes -----*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -/// \file -/// -/// Interfaces for producing common pass manager configurations and parsing -/// textual pass specifications. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_OPT_PASSES_H -#define LLVM_TOOLS_OPT_PASSES_H - -#include "llvm/ADT/StringRef.h" -#include "llvm/Analysis/CGSCCPassManager.h" -#include "llvm/IR/PassManager.h" - -namespace llvm { - -/// \brief Registers all available module analysis passes. -/// -/// This is an interface that can be used to populate a \c -/// ModuleAnalysisManager with all registered module analyses. Callers can -/// still manually register any additional analyses. -void registerModuleAnalyses(ModuleAnalysisManager &MAM); - -/// \brief Registers all available CGSCC analysis passes. -/// -/// This is an interface that can be used to populate a \c CGSCCAnalysisManager -/// with all registered CGSCC analyses. Callers can still manually register any -/// additional analyses. -void registerCGSCCAnalyses(CGSCCAnalysisManager &CGAM); - -/// \brief Registers all available function analysis passes. -/// -/// This is an interface that can be used to populate a \c -/// FunctionAnalysisManager with all registered function analyses. Callers can -/// still manually register any additional analyses. -void registerFunctionAnalyses(FunctionAnalysisManager &FAM); - -/// \brief Parse a textual pass pipeline description into a \c ModulePassManager. -/// -/// The format of the textual pass pipeline description looks something like: -/// -/// module(function(instcombine,sroa),dce,cgscc(inliner,function(...)),...) -/// -/// Pass managers have ()s describing the nest structure of passes. All passes -/// are comma separated. As a special shortcut, if the very first pass is not -/// a module pass (as a module pass manager is), this will automatically form -/// the shortest stack of pass managers that allow inserting that first pass. -/// So, assuming function passes 'fpassN', CGSCC passes 'cgpassN', and loop passes -/// 'lpassN', all of these are valid: -/// -/// fpass1,fpass2,fpass3 -/// cgpass1,cgpass2,cgpass3 -/// lpass1,lpass2,lpass3 -/// -/// And they are equivalent to the following (resp.): -/// -/// module(function(fpass1,fpass2,fpass3)) -/// module(cgscc(cgpass1,cgpass2,cgpass3)) -/// module(function(loop(lpass1,lpass2,lpass3))) -/// -/// This shortcut is especially useful for debugging and testing small pass -/// combinations. Note that these shortcuts don't introduce any other magic. If -/// the sequence of passes aren't all the exact same kind of pass, it will be -/// an error. You cannot mix different levels implicitly, you must explicitly -/// form a pass manager in which to nest passes. -bool parsePassPipeline(ModulePassManager &MPM, StringRef PipelineText, - bool VerifyEachPass = true, bool DebugLogging = false); -} - -#endif diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index 986758908061..55426e7b2743 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -20,9 +20,12 @@ #include "llvm/Analysis/CallGraphSCCPass.h" #include "llvm/Analysis/LoopPass.h" #include "llvm/Analysis/RegionPass.h" +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/Bitcode/BitcodeWriterPass.h" #include "llvm/CodeGen/CommandFlags.h" #include "llvm/IR/DataLayout.h" +#include "llvm/IR/DebugInfo.h" #include "llvm/IR/IRPrintingPasses.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/LegacyPassNameParser.h" @@ -33,9 +36,10 @@ #include "llvm/LinkAllIR.h" #include "llvm/LinkAllPasses.h" #include "llvm/MC/SubtargetFeature.h" -#include "llvm/PassManager.h" +#include "llvm/IR/LegacyPassManager.h" #include "llvm/Support/Debug.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/Host.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/PluginLoader.h" #include "llvm/Support/PrettyStackTrace.h" @@ -45,7 +49,6 @@ #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/ToolOutputFile.h" -#include "llvm/Target/TargetLibraryInfo.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Transforms/IPO/PassManagerBuilder.h" #include <algorithm> @@ -177,27 +180,33 @@ DefaultDataLayout("default-data-layout", cl::desc("data layout string to use if not specified by module"), cl::value_desc("layout-string"), cl::init("")); +static cl::opt<bool> PreserveBitcodeUseListOrder( + "preserve-bc-uselistorder", + cl::desc("Preserve use-list order when writing LLVM bitcode."), + cl::init(true), cl::Hidden); +static cl::opt<bool> PreserveAssemblyUseListOrder( + "preserve-ll-uselistorder", + cl::desc("Preserve use-list order when writing LLVM assembly."), + cl::init(false), cl::Hidden); -static inline void addPass(PassManagerBase &PM, Pass *P) { +static inline void addPass(legacy::PassManagerBase &PM, Pass *P) { // Add the pass to the pass manager... PM.add(P); // If we are verifying all of the intermediate steps, add the verifier... - if (VerifyEach) { + if (VerifyEach) PM.add(createVerifierPass()); - PM.add(createDebugInfoVerifierPass()); - } } /// This routine adds optimization passes based on selected optimization level, /// OptLevel. /// /// OptLevel - Optimization Level -static void AddOptimizationPasses(PassManagerBase &MPM,FunctionPassManager &FPM, +static void AddOptimizationPasses(legacy::PassManagerBase &MPM, + legacy::FunctionPassManager &FPM, unsigned OptLevel, unsigned SizeLevel) { - FPM.add(createVerifierPass()); // Verify that input is correct - MPM.add(createDebugInfoVerifierPass()); // Verify that debug info is correct + FPM.add(createVerifierPass()); // Verify that input is correct PassManagerBuilder Builder; Builder.OptLevel = OptLevel; @@ -229,10 +238,9 @@ static void AddOptimizationPasses(PassManagerBase &MPM,FunctionPassManager &FPM, Builder.populateModulePassManager(MPM); } -static void AddStandardLinkPasses(PassManagerBase &PM) { +static void AddStandardLinkPasses(legacy::PassManagerBase &PM) { PassManagerBuilder Builder; Builder.VerifyInput = true; - Builder.StripDebug = StripDebug; if (DisableOptimizations) Builder.OptLevel = 0; @@ -245,7 +253,7 @@ static void AddStandardLinkPasses(PassManagerBase &PM) { // CodeGen-related helper functions. // -CodeGenOpt::Level GetCodeGenOptLevel() { +static CodeGenOpt::Level GetCodeGenOptLevel() { if (OptLevelO1) return CodeGenOpt::Less; if (OptLevelO2) @@ -256,7 +264,9 @@ CodeGenOpt::Level GetCodeGenOptLevel() { } // Returns the TargetMachine instance or zero if no triple is provided. -static TargetMachine* GetTargetMachine(Triple TheTriple) { +static TargetMachine* GetTargetMachine(Triple TheTriple, StringRef CPUStr, + StringRef FeaturesStr, + const TargetOptions &Options) { std::string Error; const Target *TheTarget = TargetRegistry::lookupTarget(MArch, TheTriple, Error); @@ -265,18 +275,8 @@ static TargetMachine* GetTargetMachine(Triple TheTriple) { return nullptr; } - // Package up features to be passed to target/subtarget - std::string FeaturesStr; - if (MAttrs.size()) { - SubtargetFeatures Features; - for (unsigned i = 0; i != MAttrs.size(); ++i) - Features.AddFeature(MAttrs[i]); - FeaturesStr = Features.getString(); - } - return TheTarget->createTargetMachine(TheTriple.getTriple(), - MCPU, FeaturesStr, - InitTargetOptionsFromCodeGenFlags(), + CPUStr, FeaturesStr, Options, RelocModel, CMModel, GetCodeGenOptLevel()); } @@ -322,6 +322,8 @@ int main(int argc, char **argv) { initializeCodeGenPreparePass(Registry); initializeAtomicExpandPass(Registry); initializeRewriteSymbolsPass(Registry); + initializeWinEHPreparePass(Registry); + initializeDwarfEHPreparePass(Registry); #ifdef LINK_POLLY_INTO_TOOLS polly::initializePollyPasses(Registry); @@ -345,6 +347,19 @@ int main(int argc, char **argv) { return 1; } + // Strip debug info before running the verifier. + if (StripDebug) + StripDebugInfo(*M); + + // Immediately run the verifier to catch any problems before starting up the + // pass pipelines. Otherwise we can crash on broken code during + // doInitialization(). + if (!NoVerify && verifyModule(*M, &errs())) { + errs() << argv[0] << ": " << InputFilename + << ": error: input module is broken!\n"; + return 1; + } + // If we are supposed to override the target triple, do so now. if (!TargetTriple.empty()) M->setTargetTriple(Triple::normalize(TargetTriple)); @@ -368,6 +383,23 @@ int main(int argc, char **argv) { } } + Triple ModuleTriple(M->getTargetTriple()); + std::string CPUStr, FeaturesStr; + TargetMachine *Machine = nullptr; + const TargetOptions Options = InitTargetOptionsFromCodeGenFlags(); + + if (ModuleTriple.getArch()) { + CPUStr = getCPUStr(); + FeaturesStr = getFeaturesStr(); + Machine = GetTargetMachine(ModuleTriple, CPUStr, FeaturesStr, Options); + } + + std::unique_ptr<TargetMachine> TM(Machine); + + // Override function attributes based on CPUStr, FeaturesStr, and command line + // flags. + setFunctionAttributes(CPUStr, FeaturesStr, *M); + // If the output is set to be emitted to standard out, and standard out is a // console, print out a warning message and refuse to do it. We don't // impress anyone by spewing tons of binary goo to a terminal. @@ -389,8 +421,9 @@ int main(int argc, char **argv) { // The user has asked to use the new pass manager and provided a pipeline // string. Hand off the rest of the functionality to the new code for that // layer. - return runPassPipeline(argv[0], Context, *M, Out.get(), PassPipeline, - OK, VK) + return runPassPipeline(argv[0], Context, *M, TM.get(), Out.get(), + PassPipeline, OK, VK, PreserveAssemblyUseListOrder, + PreserveBitcodeUseListOrder) ? 0 : 1; } @@ -398,44 +431,31 @@ int main(int argc, char **argv) { // Create a PassManager to hold and optimize the collection of passes we are // about to build. // - PassManager Passes; + legacy::PassManager Passes; // Add an appropriate TargetLibraryInfo pass for the module's triple. - TargetLibraryInfo *TLI = new TargetLibraryInfo(Triple(M->getTargetTriple())); + TargetLibraryInfoImpl TLII(ModuleTriple); // The -disable-simplify-libcalls flag actually disables all builtin optzns. if (DisableSimplifyLibCalls) - TLI->disableAllFunctions(); - Passes.add(TLI); + TLII.disableAllFunctions(); + Passes.add(new TargetLibraryInfoWrapperPass(TLII)); // Add an appropriate DataLayout instance for this module. - const DataLayout *DL = M->getDataLayout(); - if (!DL && !DefaultDataLayout.empty()) { + const DataLayout &DL = M->getDataLayout(); + if (DL.isDefault() && !DefaultDataLayout.empty()) { M->setDataLayout(DefaultDataLayout); - DL = M->getDataLayout(); } - if (DL) - Passes.add(new DataLayoutPass()); - - Triple ModuleTriple(M->getTargetTriple()); - TargetMachine *Machine = nullptr; - if (ModuleTriple.getArch()) - Machine = GetTargetMachine(Triple(ModuleTriple)); - std::unique_ptr<TargetMachine> TM(Machine); - // Add internal analysis passes from the target machine. - if (TM) - TM->addAnalysisPasses(Passes); + Passes.add(createTargetTransformInfoWrapperPass(TM ? TM->getTargetIRAnalysis() + : TargetIRAnalysis())); - std::unique_ptr<FunctionPassManager> FPasses; + std::unique_ptr<legacy::FunctionPassManager> FPasses; if (OptLevelO1 || OptLevelO2 || OptLevelOs || OptLevelOz || OptLevelO3) { - FPasses.reset(new FunctionPassManager(M.get())); - if (DL) - FPasses->add(new DataLayoutPass()); - if (TM) - TM->addAnalysisPasses(*FPasses); - + FPasses.reset(new legacy::FunctionPassManager(M.get())); + FPasses->add(createTargetTransformInfoWrapperPass( + TM ? TM->getTargetIRAnalysis() : TargetIRAnalysis())); } if (PrintBreakpoints) { @@ -456,10 +476,6 @@ int main(int argc, char **argv) { NoOutput = true; } - // If the -strip-debug command line option was specified, add it. - if (StripDebug) - addPass(Passes, createStripSymbolsPass(true)); - // Create a new optimization pass for each one specified on the command line for (unsigned i = 0; i < PassList.size(); ++i) { if (StandardLinkOpts && @@ -531,7 +547,8 @@ int main(int argc, char **argv) { } if (PrintEachXForm) - Passes.add(createPrintModulePass(errs())); + Passes.add( + createPrintModulePass(errs(), "", PreserveAssemblyUseListOrder)); } if (StandardLinkOpts) { @@ -562,17 +579,17 @@ int main(int argc, char **argv) { } // Check that the module is well formed on completion of optimization - if (!NoVerify && !VerifyEach) { + if (!NoVerify && !VerifyEach) Passes.add(createVerifierPass()); - Passes.add(createDebugInfoVerifierPass()); - } // Write bitcode or assembly to the output as the last step... if (!NoOutput && !AnalyzeOnly) { if (OutputAssembly) - Passes.add(createPrintModulePass(Out->os())); + Passes.add( + createPrintModulePass(Out->os(), "", PreserveAssemblyUseListOrder)); else - Passes.add(createBitcodeWriterPass(Out->os())); + Passes.add( + createBitcodeWriterPass(Out->os(), PreserveBitcodeUseListOrder)); } // Before executing passes, print the final values of the LLVM options. diff --git a/tools/verify-uselistorder/verify-uselistorder.cpp b/tools/verify-uselistorder/verify-uselistorder.cpp index a6536083e880..795d035d3df4 100644 --- a/tools/verify-uselistorder/verify-uselistorder.cpp +++ b/tools/verify-uselistorder/verify-uselistorder.cpp @@ -47,12 +47,13 @@ #include "llvm/Support/Signals.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/SystemUtils.h" +#include "llvm/Support/raw_ostream.h" #include <random> #include <vector> using namespace llvm; -#define DEBUG_TYPE "use-list-order" +#define DEBUG_TYPE "uselistorder" static cl::opt<std::string> InputFilename(cl::Positional, cl::desc("<input bitcode file>"), @@ -108,16 +109,16 @@ struct ValueMapping { bool TempFile::init(const std::string &Ext) { SmallVector<char, 64> Vector; DEBUG(dbgs() << " - create-temp-file\n"); - if (auto EC = sys::fs::createTemporaryFile("use-list-order", Ext, Vector)) { - (void)EC; - DEBUG(dbgs() << "error: " << EC.message() << "\n"); + if (auto EC = sys::fs::createTemporaryFile("uselistorder", Ext, Vector)) { + errs() << "verify-uselistorder: error: " << EC.message() << "\n"; return true; } assert(!Vector.empty()); Filename.assign(Vector.data(), Vector.data() + Vector.size()); Remover.setFile(Filename, !SaveTemps); - DEBUG(dbgs() << " - filename = " << Filename << "\n"); + if (SaveTemps) + outs() << " - filename = " << Filename << "\n"; return false; } @@ -126,11 +127,11 @@ bool TempFile::writeBitcode(const Module &M) const { std::error_code EC; raw_fd_ostream OS(Filename, EC, sys::fs::F_None); if (EC) { - DEBUG(dbgs() << "error: " << EC.message() << "\n"); + errs() << "verify-uselistorder: error: " << EC.message() << "\n"; return true; } - WriteBitcodeToFile(&M, OS); + WriteBitcodeToFile(&M, OS, /* ShouldPreserveUseListOrder */ true); return false; } @@ -139,11 +140,11 @@ bool TempFile::writeAssembly(const Module &M) const { std::error_code EC; raw_fd_ostream OS(Filename, EC, sys::fs::F_Text); if (EC) { - DEBUG(dbgs() << "error: " << EC.message() << "\n"); + errs() << "verify-uselistorder: error: " << EC.message() << "\n"; return true; } - OS << M; + M.print(OS, nullptr, /* ShouldPreserveUseListOrder */ true); return false; } @@ -152,7 +153,8 @@ std::unique_ptr<Module> TempFile::readBitcode(LLVMContext &Context) const { ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOr = MemoryBuffer::getFile(Filename); if (!BufferOr) { - DEBUG(dbgs() << "error: " << BufferOr.getError().message() << "\n"); + errs() << "verify-uselistorder: error: " << BufferOr.getError().message() + << "\n"; return nullptr; } @@ -160,7 +162,8 @@ std::unique_ptr<Module> TempFile::readBitcode(LLVMContext &Context) const { ErrorOr<Module *> ModuleOr = parseBitcodeFile(Buffer->getMemBufferRef(), Context); if (!ModuleOr) { - DEBUG(dbgs() << "error: " << ModuleOr.getError().message() << "\n"); + errs() << "verify-uselistorder: error: " << ModuleOr.getError().message() + << "\n"; return nullptr; } return std::unique_ptr<Module>(ModuleOr.get()); @@ -171,7 +174,7 @@ std::unique_ptr<Module> TempFile::readAssembly(LLVMContext &Context) const { SMDiagnostic Err; std::unique_ptr<Module> M = parseAssemblyFile(Filename, Err, Context); if (!M.get()) - DEBUG(dbgs() << "error: "; Err.print("verify-use-list-order", dbgs())); + Err.print("verify-uselistorder", errs()); return M; } @@ -342,7 +345,6 @@ static void verifyAfterRoundTrip(const Module &M, report_fatal_error("use-list order changed"); } static void verifyBitcodeUseListOrder(const Module &M) { - errs() << "*** verify-use-list-order: bitcode ***\n"; TempFile F; if (F.init("bc")) report_fatal_error("failed to initialize bitcode file"); @@ -355,7 +357,6 @@ static void verifyBitcodeUseListOrder(const Module &M) { } static void verifyAssemblyUseListOrder(const Module &M) { - errs() << "*** verify-use-list-order: assembly ***\n"; TempFile F; if (F.init("ll")) report_fatal_error("failed to initialize assembly file"); @@ -368,7 +369,9 @@ static void verifyAssemblyUseListOrder(const Module &M) { } static void verifyUseListOrder(const Module &M) { + outs() << "verify bitcode\n"; verifyBitcodeUseListOrder(M); + outs() << "verify assembly\n"; verifyAssemblyUseListOrder(M); } @@ -497,7 +500,6 @@ static void changeUseLists(Module &M, Changer changeValueUseList) { } static void shuffleUseLists(Module &M, unsigned SeedOffset) { - errs() << "*** shuffle-use-lists ***\n"; std::minstd_rand0 Gen(std::minstd_rand0::default_seed + SeedOffset); DenseSet<Value *> Seen; changeUseLists(M, [&](Value *V) { shuffleValueUseLists(V, Gen, Seen); }); @@ -505,7 +507,6 @@ static void shuffleUseLists(Module &M, unsigned SeedOffset) { } static void reverseUseLists(Module &M) { - errs() << "*** reverse-use-lists ***\n"; DenseSet<Value *> Seen; changeUseLists(M, [&](Value *V) { reverseValueUseLists(V, Seen); }); DEBUG(dbgs() << "\n"); @@ -533,33 +534,29 @@ int main(int argc, char **argv) { Err.print(argv[0], errs()); return 1; } - if (verifyModule(*M, &errs())) - report_fatal_error("verification failed"); - - errs() << "*** verify-use-list-order ***\n"; - // Can't verify if order isn't preserved. - if (!shouldPreserveBitcodeUseListOrder()) { - errs() << "warning: forcing -preserve-bc-use-list-order\n"; - setPreserveBitcodeUseListOrder(true); - } - if (!shouldPreserveAssemblyUseListOrder()) { - errs() << "warning: forcing -preserve-ll-use-list-order\n"; - setPreserveAssemblyUseListOrder(true); + if (verifyModule(*M, &errs())) { + errs() << argv[0] << ": " << InputFilename + << ": error: input module is broken!\n"; + return 1; } // Verify the use lists now and after reversing them. + outs() << "*** verify-uselistorder ***\n"; verifyUseListOrder(*M); + outs() << "reverse\n"; reverseUseLists(*M); verifyUseListOrder(*M); for (unsigned I = 0, E = NumShuffles; I != E; ++I) { - errs() << "*** shuffle iteration: " << I + 1 << " of " << E << " ***\n"; + outs() << "\n"; // Shuffle with a different (deterministic) seed each time. + outs() << "shuffle (" << I + 1 << " of " << E << ")\n"; shuffleUseLists(*M, I); // Verify again before and after reversing. verifyUseListOrder(*M); + outs() << "reverse\n"; reverseUseLists(*M); verifyUseListOrder(*M); } diff --git a/tools/yaml2obj/yaml2coff.cpp b/tools/yaml2obj/yaml2coff.cpp index 8322b2ff0a0d..61d985192552 100644 --- a/tools/yaml2obj/yaml2coff.cpp +++ b/tools/yaml2obj/yaml2coff.cpp @@ -253,10 +253,7 @@ binary_le_impl<value_type> binary_le(value_type V) { return binary_le_impl<value_type>(V); } -template <size_t NumBytes> -struct zeros_impl { - zeros_impl() {} -}; +template <size_t NumBytes> struct zeros_impl {}; template <size_t NumBytes> raw_ostream &operator<<(raw_ostream &OS, const zeros_impl<NumBytes> &) { @@ -282,7 +279,7 @@ raw_ostream &operator<<(raw_ostream &OS, const num_zeros_impl &NZI) { return OS; } -num_zeros_impl num_zeros(size_t N) { +static num_zeros_impl num_zeros(size_t N) { num_zeros_impl NZI(N); return NZI; } diff --git a/tools/yaml2obj/yaml2elf.cpp b/tools/yaml2obj/yaml2elf.cpp index 44c8c12da89d..338658805638 100644 --- a/tools/yaml2obj/yaml2elf.cpp +++ b/tools/yaml2obj/yaml2elf.cpp @@ -131,6 +131,11 @@ class ELFState { bool writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::RelocationSection &Section, ContiguousBlobAccumulator &CBA); + bool writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::Group &Group, + ContiguousBlobAccumulator &CBA); + bool writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::MipsABIFlags &Section, + ContiguousBlobAccumulator &CBA); // - SHT_NULL entry (placed first, i.e. 0'th entry) // - symbol table (.symtab) (placed third to last) @@ -223,6 +228,19 @@ bool ELFState<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders, if (!writeSectionContent(SHeader, *S, CBA)) return false; + } else if (auto S = dyn_cast<ELFYAML::Group>(Sec.get())) { + unsigned SymIdx; + if (SymN2I.lookup(S->Info, SymIdx)) { + errs() << "error: Unknown symbol referenced: '" << S->Info + << "' at YAML section '" << S->Name << "'.\n"; + return false; + } + SHeader.sh_info = SymIdx; + if (!writeSectionContent(SHeader, *S, CBA)) + return false; + } else if (auto S = dyn_cast<ELFYAML::MipsABIFlags>(Sec.get())) { + if (!writeSectionContent(SHeader, *S, CBA)) + return false; } else llvm_unreachable("Unknown section type"); @@ -321,16 +339,20 @@ ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader, SHeader.sh_size = Section.Size; } +static bool isMips64EL(const ELFYAML::Object &Doc) { + return Doc.Header.Machine == ELFYAML::ELF_EM(llvm::ELF::EM_MIPS) && + Doc.Header.Class == ELFYAML::ELF_ELFCLASS(ELF::ELFCLASS64) && + Doc.Header.Data == ELFYAML::ELF_ELFDATA(ELF::ELFDATA2LSB); +} + template <class ELFT> bool ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::RelocationSection &Section, ContiguousBlobAccumulator &CBA) { - if (Section.Type != llvm::ELF::SHT_REL && - Section.Type != llvm::ELF::SHT_RELA) { - errs() << "error: Invalid relocation section type.\n"; - return false; - } + assert((Section.Type == llvm::ELF::SHT_REL || + Section.Type == llvm::ELF::SHT_RELA) && + "Section type is not SHT_REL nor SHT_RELA"); bool IsRela = Section.Type == llvm::ELF::SHT_RELA; SHeader.sh_entsize = IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel); @@ -339,31 +361,89 @@ ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader, auto &OS = CBA.getOSAndAlignedOffset(SHeader.sh_offset); for (const auto &Rel : Section.Relocations) { - unsigned SymIdx; - if (SymN2I.lookup(Rel.Symbol, SymIdx)) { - errs() << "error: Unknown symbol referenced: '" << Rel.Symbol - << "' at YAML relocation.\n"; - return false; - } + unsigned SymIdx = 0; + // Some special relocation, R_ARM_v4BX for instance, does not have + // an external reference. So it ignores the return value of lookup() + // here. + SymN2I.lookup(Rel.Symbol, SymIdx); if (IsRela) { Elf_Rela REntry; zero(REntry); REntry.r_offset = Rel.Offset; REntry.r_addend = Rel.Addend; - REntry.setSymbolAndType(SymIdx, Rel.Type); + REntry.setSymbolAndType(SymIdx, Rel.Type, isMips64EL(Doc)); OS.write((const char *)&REntry, sizeof(REntry)); } else { Elf_Rel REntry; zero(REntry); REntry.r_offset = Rel.Offset; - REntry.setSymbolAndType(SymIdx, Rel.Type); + REntry.setSymbolAndType(SymIdx, Rel.Type, isMips64EL(Doc)); OS.write((const char *)&REntry, sizeof(REntry)); } } return true; } +template <class ELFT> +bool ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::Group &Section, + ContiguousBlobAccumulator &CBA) { + typedef typename object::ELFFile<ELFT>::Elf_Word Elf_Word; + assert(Section.Type == llvm::ELF::SHT_GROUP && + "Section type is not SHT_GROUP"); + + SHeader.sh_entsize = sizeof(Elf_Word); + SHeader.sh_size = SHeader.sh_entsize * Section.Members.size(); + + auto &OS = CBA.getOSAndAlignedOffset(SHeader.sh_offset); + + for (auto member : Section.Members) { + Elf_Word SIdx; + unsigned int sectionIndex = 0; + if (member.sectionNameOrType == "GRP_COMDAT") + sectionIndex = llvm::ELF::GRP_COMDAT; + else if (SN2I.lookup(member.sectionNameOrType, sectionIndex)) { + errs() << "error: Unknown section referenced: '" + << member.sectionNameOrType << "' at YAML section' " + << Section.Name << "\n"; + return false; + } + SIdx = sectionIndex; + OS.write((const char *)&SIdx, sizeof(SIdx)); + } + return true; +} + +template <class ELFT> +bool ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::MipsABIFlags &Section, + ContiguousBlobAccumulator &CBA) { + assert(Section.Type == llvm::ELF::SHT_MIPS_ABIFLAGS && + "Section type is not SHT_MIPS_ABIFLAGS"); + + object::Elf_Mips_ABIFlags<ELFT> Flags; + zero(Flags); + SHeader.sh_entsize = sizeof(Flags); + SHeader.sh_size = SHeader.sh_entsize; + + auto &OS = CBA.getOSAndAlignedOffset(SHeader.sh_offset); + Flags.version = Section.Version; + Flags.isa_level = Section.ISALevel; + Flags.isa_rev = Section.ISARevision; + Flags.gpr_size = Section.GPRSize; + Flags.cpr1_size = Section.CPR1Size; + Flags.cpr2_size = Section.CPR2Size; + Flags.fp_abi = Section.FpABI; + Flags.isa_ext = Section.ISAExtension; + Flags.ases = Section.ASEs; + Flags.flags1 = Section.Flags1; + Flags.flags2 = Section.Flags2; + OS.write((const char *)&Flags, sizeof(Flags)); + + return true; +} + template <class ELFT> bool ELFState<ELFT>::buildSectionIndex() { SN2I.addName(".symtab", getDotSymTabSecNo()); SN2I.addName(".strtab", getDotStrTabSecNo()); diff --git a/tools/yaml2obj/yaml2obj.cpp b/tools/yaml2obj/yaml2obj.cpp index 375cd89849c3..af4d86890672 100644 --- a/tools/yaml2obj/yaml2obj.cpp +++ b/tools/yaml2obj/yaml2obj.cpp @@ -62,7 +62,8 @@ static cl::opt<std::string> OutputFilename("o", cl::desc("Output filename"), typedef int (*ConvertFuncPtr)(yaml::Input & YIn, raw_ostream &Out); -int convertYAML(yaml::Input & YIn, raw_ostream &Out, ConvertFuncPtr Convert) { +static int convertYAML(yaml::Input &YIn, raw_ostream &Out, + ConvertFuncPtr Convert) { unsigned CurDocNum = 0; do { if (++CurDocNum == DocNum) |