diff options
Diffstat (limited to 'tools')
29 files changed, 3484 insertions, 214 deletions
diff --git a/tools/bugpoint/BugDriver.cpp b/tools/bugpoint/BugDriver.cpp index 865cb51..43f4c29 100644 --- a/tools/bugpoint/BugDriver.cpp +++ b/tools/bugpoint/BugDriver.cpp @@ -93,7 +93,7 @@ std::unique_ptr<Module> llvm::parseInputFile(StringRef Filename, } if (verifyModule(*Result, &errs())) { - errs() << "bugpoint: " << Filename << ": error: does not verify\n"; + errs() << "bugpoint: " << Filename << ": error: input module is broken!\n"; return std::unique_ptr<Module>(); } diff --git a/tools/bugpoint/Miscompilation.cpp b/tools/bugpoint/Miscompilation.cpp index 98061bd..53631d2 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); diff --git a/tools/bugpoint/OptimizerDriver.cpp b/tools/bugpoint/OptimizerDriver.cpp index 481f343..344e7b5 100644 --- a/tools/bugpoint/OptimizerDriver.cpp +++ b/tools/bugpoint/OptimizerDriver.cpp @@ -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 454724a..5d67a94 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/gold/gold-plugin.cpp b/tools/gold/gold-plugin.cpp index 93ce3bc..724e93c 100644 --- a/tools/gold/gold-plugin.cpp +++ b/tools/gold/gold-plugin.cpp @@ -31,7 +31,7 @@ #include "llvm/Linker/Linker.h" #include "llvm/MC/SubtargetFeature.h" #include "llvm/Object/IRObjectFile.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" @@ -739,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) { @@ -804,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); diff --git a/tools/llc/llc.cpp b/tools/llc/llc.cpp index 55a45dc..ab50f2a 100644 --- a/tools/llc/llc.cpp +++ b/tools/llc/llc.cpp @@ -25,6 +25,7 @@ #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" @@ -211,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); @@ -225,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)); @@ -247,13 +250,31 @@ static int compileModule(char **argv, LLVMContext &Context) { // Package up features to be passed to target/subtarget std::string FeaturesStr; - if (MAttrs.size()) { + if (!MAttrs.empty() || MCPU == "native") { SubtargetFeatures Features; + + // If user asked for the 'native' CPU, we need to autodetect features. + // This is necessary for x86 where the CPU might not support all the + // features the autodetected CPU name lists in the target. For example, + // not all Sandybridge processors support AVX. + if (MCPU == "native") { + StringMap<bool> HostFeatures; + if (sys::getHostCPUFeatures(HostFeatures)) + for (auto &F : HostFeatures) + Features.AddFeature(F.first(), F.second); + } + for (unsigned i = 0; i != MAttrs.size(); ++i) Features.AddFeature(MAttrs[i]); FeaturesStr = Features.getString(); } + // 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(); + CodeGenOpt::Level OLvl = CodeGenOpt::Default; switch (OptLevel) { default: @@ -314,7 +335,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; @@ -337,8 +364,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/OrcLazyJIT.cpp b/tools/lli/OrcLazyJIT.cpp index 4a8d3b9..ff8baf0 100644 --- a/tools/lli/OrcLazyJIT.cpp +++ b/tools/lli/OrcLazyJIT.cpp @@ -9,34 +9,130 @@ #include "OrcLazyJIT.h" #include "llvm/ExecutionEngine/Orc/OrcTargetSupport.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/DynamicLibrary.h" +#include <system_error> using namespace llvm; -std::unique_ptr<OrcLazyJIT::CompileCallbackMgr> -OrcLazyJIT::createCallbackMgr(Triple T, LLVMContext &Context) { +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: - // Flag error. - Error = true; - return nullptr; + default: return nullptr; case Triple::x86_64: { - typedef orc::JITCompileCallbackManager<CompileLayerT, + typedef orc::JITCompileCallbackManager<IRDumpLayerT, orc::OrcX86_64> CCMgrT; - return make_unique<CCMgrT>(CompileLayer, Context, 0, 64); + 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 std::move(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 std::move(M); + }; + + case DumpKind::DumpModsToStdErr: + return [](std::unique_ptr<Module> M) { + dbgs() << "----- Module Start -----\n" << *M + << "----- Module End -----\n"; + + return std::move(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 std::move(M); + }; + } + llvm_unreachable("Unknown DumpKind"); +} + int llvm::runOrcLazyJIT(std::unique_ptr<Module> M, int ArgC, char* ArgV[]) { - OrcLazyJIT J(std::unique_ptr<TargetMachine>(EngineBuilder().selectTarget()), - getGlobalContext()); + // 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 (!J.Ok()) { - errs() << "Could not construct JIT.\n"; + // 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"); @@ -46,8 +142,6 @@ int llvm::runOrcLazyJIT(std::unique_ptr<Module> M, int ArgC, char* ArgV[]) { } typedef int (*MainFnPtr)(int, char*[]); - auto Main = reinterpret_cast<MainFnPtr>( - static_cast<uintptr_t>(MainSym.getAddress())); - + auto Main = OrcLazyJIT::fromTargetAddress<MainFnPtr>(MainSym.getAddress()); return Main(ArgC, ArgV); } diff --git a/tools/lli/OrcLazyJIT.h b/tools/lli/OrcLazyJIT.h index 76e1ac6..2b2db6e 100644 --- a/tools/lli/OrcLazyJIT.h +++ b/tools/lli/OrcLazyJIT.h @@ -18,9 +18,12 @@ #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/LazyEmittingLayer.h" #include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h" +#include "llvm/ExecutionEngine/RTDyldMemoryManager.h" #include "llvm/IR/LLVMContext.h" namespace llvm { @@ -31,30 +34,96 @@ public: typedef orc::JITCompileCallbackManagerBase CompileCallbackMgr; typedef orc::ObjectLinkingLayer<> ObjLayerT; typedef orc::IRCompileLayer<ObjLayerT> CompileLayerT; - typedef orc::LazyEmittingLayer<CompileLayerT> LazyEmitLayerT; + typedef std::function<std::unique_ptr<Module>(std::unique_ptr<Module>)> + TransformFtor; + typedef orc::IRTransformLayer<CompileLayerT, TransformFtor> IRDumpLayerT; + typedef orc::LazyEmittingLayer<IRDumpLayerT> LazyEmitLayerT; typedef orc::CompileOnDemandLayer<LazyEmitLayerT, CompileCallbackMgr> CODLayerT; typedef CODLayerT::ModuleSetHandleT ModuleHandleT; - OrcLazyJIT(std::unique_ptr<TargetMachine> TM, LLVMContext &Context) - : Error(false), TM(std::move(TM)), + 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([](){ return llvm::make_unique<SectionMemoryManager>(); }), + ObjectLayer(), CompileLayer(ObjectLayer, orc::SimpleCompiler(*this->TM)), - LazyEmitLayer(CompileLayer), - CCMgr(createCallbackMgr(Triple(this->TM->getTargetTriple()), Context)), - CODLayer(LazyEmitLayer, *CCMgr) { } + IRDumpLayer(CompileLayer, createDebugDumper()), + LazyEmitLayer(IRDumpLayer), + CCMgr(BuildCallbackMgr(IRDumpLayer, CCMgrMemMgr, Context)), + CODLayer(LazyEmitLayer, *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); + } - bool Ok() const { return !Error; } + 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)); - return CODLayer.addModuleSet(std::move(S)); + 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) { @@ -67,9 +136,6 @@ public: private: - std::unique_ptr<CompileCallbackMgr> - createCallbackMgr(Triple T, LLVMContext &Context); - std::string mangle(const std::string &Name) { std::string MangledName; { @@ -79,15 +145,21 @@ private: return MangledName; } - bool Error; + static TransformFtor createDebugDumper(); + std::unique_ptr<TargetMachine> TM; Mangler Mang; + SectionMemoryManager CCMgrMemMgr; ObjLayerT ObjectLayer; CompileLayerT CompileLayer; + IRDumpLayerT IRDumpLayer; LazyEmitLayerT LazyEmitLayer; 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[]); diff --git a/tools/lli/RemoteMemoryManager.h b/tools/lli/RemoteMemoryManager.h index 895bcda..5733fa5 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 bb621f5..afe8570 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 47ce2c0..c1e3522 100644 --- a/tools/lli/lli.cpp +++ b/tools/lli/lli.cpp @@ -271,7 +271,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(); diff --git a/tools/llvm-as/llvm-as.cpp b/tools/llvm-as/llvm-as.cpp index 5ccf505..c6b3f93 100644 --- a/tools/llvm-as/llvm-as.cpp +++ b/tools/llvm-as/llvm-as.cpp @@ -51,6 +51,11 @@ 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()) { @@ -78,7 +83,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-cxxdump/llvm-cxxdump.cpp b/tools/llvm-cxxdump/llvm-cxxdump.cpp index aeb977a..447d55a 100644 --- a/tools/llvm-cxxdump/llvm-cxxdump.cpp +++ b/tools/llvm-cxxdump/llvm-cxxdump.cpp @@ -355,7 +355,7 @@ static void dumpCXXData(const ObjectFile *Obj) { 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) { @@ -363,7 +363,7 @@ static void dumpCXXData(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'; @@ -373,7 +373,7 @@ static void dumpCXXData(const ObjectFile *Obj) { 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'; @@ -381,14 +381,13 @@ static void dumpCXXData(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'; @@ -400,7 +399,7 @@ static void dumpCXXData(const ObjectFile *Obj) { 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'; @@ -410,7 +409,7 @@ static void dumpCXXData(const ObjectFile *Obj) { /*UseHexEscapes=*/true) << '\n'; } - for (const std::pair<StringRef, ThrowInfo> &TIPair : TIs) { + for (const auto &TIPair : TIs) { StringRef TIName = TIPair.first; const ThrowInfo &TI = TIPair.second; auto dumpThrowInfoFlag = [&](const char *Name, uint32_t Flag) { @@ -429,7 +428,7 @@ static void dumpCXXData(const ObjectFile *Obj) { dumpThrowInfoSymbol("ForwardCompat", 8); dumpThrowInfoSymbol("CatchableTypeArray", 12); } - for (const std::pair<StringRef, CatchableTypeArray> &CTAPair : CTAs) { + for (const auto &CTAPair : CTAs) { StringRef CTAName = CTAPair.first; const CatchableTypeArray &CTA = CTAPair.second; @@ -441,7 +440,7 @@ static void dumpCXXData(const ObjectFile *Obj) { I != E; ++I) outs() << CTAName << '[' << Idx++ << "]: " << I->second << '\n'; } - for (const std::pair<StringRef, CatchableType> &CTPair : CTs) { + for (const auto &CTPair : CTs) { StringRef CTName = CTPair.first; const CatchableType &CT = CTPair.second; auto dumpCatchableTypeFlag = [&](const char *Name, uint32_t Flag) { @@ -464,14 +463,13 @@ static void dumpCXXData(const ObjectFile *Obj) { << "[CopyCtor]: " << (CT.Symbols[1].empty() ? "null" : CT.Symbols[1]) << '\n'; } - for (const std::pair<std::pair<StringRef, uint64_t>, StringRef> &VTTPair : - VTTEntries) { + 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'; } diff --git a/tools/llvm-dis/llvm-dis.cpp b/tools/llvm-dis/llvm-dis.cpp index 1c3a9ce..1ff2302 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 (MDLocation *IDL = DL.getInlinedAt()) { + OS << "@"; + printDebugLoc(IDL, OS); } } class CommentWriter : public AssemblyAnnotationWriter { @@ -81,8 +83,7 @@ public: OS << "; [#uses=" << V.getNumUses() << " type=" << *V.getType() << "]"; // Output # uses and type } 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; @@ -98,7 +99,7 @@ public: OS.PadToColumn(50); OS << ";"; } - OS << " [debug variable = " << Var.getName() << "]"; + OS << " [debug variable = " << Var->getName() << "]"; } else if (const DbgValueInst *DVI = dyn_cast<DbgValueInst>(I)) { DIVariable Var(DVI->getVariable()); @@ -106,7 +107,7 @@ public: OS.PadToColumn(50); OS << ";"; } - OS << " [debug variable = " << Var.getName() << "]"; + OS << " [debug variable = " << Var->getName() << "]"; } } } @@ -193,7 +194,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-extract/llvm-extract.cpp b/tools/llvm-extract/llvm-extract.cpp index 8bfd319..936496c 100644 --- a/tools/llvm-extract/llvm-extract.cpp +++ b/tools/llvm-extract/llvm-extract.cpp @@ -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(); @@ -264,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-link/llvm-link.cpp b/tools/llvm-link/llvm-link.cpp index e52191a..bcefc0b 100644 --- a/tools/llvm-link/llvm-link.cpp +++ b/tools/llvm-link/llvm-link.cpp @@ -15,6 +15,7 @@ #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" @@ -58,6 +59,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... // @@ -69,6 +80,9 @@ loadFile(const char *argv0, const std::string &FN, LLVMContext &Context) { if (!Result) Err.print(argv0, errs()); + Result->materializeMetadata(); + UpgradeDebugInfo(*Result); + return Result; } @@ -112,9 +126,9 @@ int main(int argc, char **argv) { return 1; } - if (verifyModule(*M)) { - errs() << argv[0] << ": input module '" << InputFilenames[i] - << "' is broken!\n"; + if (verifyModule(*M, &errs())) { + errs() << argv[0] << ": " << InputFilenames[i] + << ": error: input module is broken!\n"; return 1; } @@ -133,16 +147,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-mc/llvm-mc.cpp b/tools/llvm-mc/llvm-mc.cpp index 4f9b6fc..6a8b493 100644 --- a/tools/llvm-mc/llvm-mc.cpp +++ b/tools/llvm-mc/llvm-mc.cpp @@ -439,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()); @@ -448,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); @@ -461,17 +462,24 @@ int main(int argc, char **argv) { 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(TheTarget->createNullStreamer(Ctx)); } else { assert(FileType == OFT_ObjectFile && "Invalid file type!"); + + 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(TheTriple, Ctx, *MAB, FOS, CE, + Str.reset(TheTarget->createMCObjectStreamer(TheTriple, Ctx, *MAB, *OS, CE, *STI, RelaxAll, /*DWARFMustBeAtTheEnd*/ false)); if (NoExecStack) diff --git a/tools/llvm-objdump/MachODump.cpp b/tools/llvm-objdump/MachODump.cpp index 86504e6..a491a37 100644 --- a/tools/llvm-objdump/MachODump.cpp +++ b/tools/llvm-objdump/MachODump.cpp @@ -99,6 +99,9 @@ cl::list<std::string> 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 " @@ -119,6 +122,11 @@ cl::opt<bool> cl::desc("Print the info for Mach-O objects in " "non-verbose or numeric form (requires -macho)")); +cl::opt<bool> + 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")); @@ -127,7 +135,6 @@ 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"), cl::ZeroOrMore); @@ -605,7 +612,8 @@ static void CreateSymbolAddressMap(MachOObjectFile *O, Symbol.getAddress(Address); StringRef SymName; Symbol.getName(SymName); - (*AddrMap)[Address] = SymName; + if (!SymName.startswith(".objc")) + (*AddrMap)[Address] = SymName; } } } @@ -1017,6 +1025,8 @@ static void DumpRawSectionContents(MachOObjectFile *O, const char *sect, 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) { @@ -1043,8 +1053,7 @@ static void DumpSectionContents(StringRef Filename, MachOObjectFile *O, StringRef SegName = O->getSectionFinalSegmentName(Ref); if ((DumpSegName.empty() || SegName == DumpSegName) && (SectName == DumpSectName)) { - outs() << "Contents of (" << SegName << "," << SectName - << ") section\n"; + uint32_t section_flags; if (O->is64Bit()) { const MachO::section_64 Sec = O->getSection64(Ref); @@ -1062,6 +1071,14 @@ static void DumpSectionContents(StringRef Filename, MachOObjectFile *O, 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)) { @@ -1072,6 +1089,10 @@ static void DumpSectionContents(StringRef Filename, MachOObjectFile *O, 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); @@ -1089,8 +1110,8 @@ static void DumpSectionContents(StringRef Filename, MachOObjectFile *O, DumpLiteral8Section(O, sect, sect_size, sect_addr, !NoLeadingAddr); break; case MachO::S_16BYTE_LITERALS: - DumpLiteral16Section(O, sect, sect_size, sect_addr, !NoLeadingAddr); - break; + DumpLiteral16Section(O, sect, sect_size, sect_addr, !NoLeadingAddr); + break; case MachO::S_LITERAL_POINTERS: DumpLiteralPointerSection(O, Section, sect, sect_size, sect_addr, !NoLeadingAddr); @@ -1169,6 +1190,8 @@ static bool checkMachOAndArchFlags(ObjectFile *O, StringRef Filename) { return true; } +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 // the file name and header info and then processes it according to the @@ -1181,7 +1204,8 @@ static void ProcessMachO(StringRef Filename, MachOObjectFile *MachOOF, // UniversalHeaders or ArchiveHeaders. if (Disassemble || PrivateHeaders || ExportsTrie || Rebase || Bind || LazyBind || WeakBind || IndirectSymbols || DataInCode || LinkOptHints || - DylibsUsed || DylibId || DumpSections.size() != 0) { + DylibsUsed || DylibId || ObjcMetaData || + (DumpSections.size() != 0 && !Raw)) { outs() << Filename; if (!ArchiveMemberName.empty()) outs() << '(' << ArchiveMemberName << ')'; @@ -1218,6 +1242,8 @@ static void ProcessMachO(StringRef Filename, MachOObjectFile *MachOOF, printMachOUnwindInfo(MachOOF); if (PrivateHeaders) printMachOFileHeader(MachOOF); + if (ObjcMetaData) + printObjcMetaData(MachOOF, !NonVerbose); if (ExportsTrie) printExportsTrie(MachOOF); if (Rebase) @@ -2385,13 +2411,22 @@ static uint64_t GuessPointerPointer(uint64_t ReferenceValue, // section nullptr is returned. static const char *get_pointer_64(uint64_t Address, uint32_t &offset, uint32_t &left, SectionRef &S, - DisassembleInfo *info) { + 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; @@ -2404,11 +2439,22 @@ static const char *get_pointer_64(uint64_t Address, uint32_t &offset, 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. -static 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) return nullptr; @@ -2442,6 +2488,8 @@ static 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()) { @@ -2459,17 +2507,21 @@ static 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->AddrMap); + // 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 . @@ -2491,6 +2543,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; @@ -2505,6 +2565,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); @@ -2520,6 +2850,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); @@ -2534,6 +2872,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); @@ -2642,6 +3212,2411 @@ static 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 @@ -3022,7 +5997,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, } 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. @@ -3070,8 +6045,8 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, } 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); } @@ -3337,9 +6312,9 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, 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. @@ -3398,7 +6373,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, outs() << "\t"; 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(); diff --git a/tools/llvm-objdump/llvm-objdump.cpp b/tools/llvm-objdump/llvm-objdump.cpp index 7bec062..71de8ad 100644 --- a/tools/llvm-objdump/llvm-objdump.cpp +++ b/tools/llvm-objdump/llvm-objdump.cpp @@ -274,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'; @@ -401,7 +401,7 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { outs() << "\t"; DumpBytes(ArrayRef<uint8_t>(Bytes.data() + Index, Size)); } - IP->printInst(&Inst, outs(), ""); + IP->printInst(&Inst, outs(), "", *STI); outs() << CommentStream.str(); Comments.clear(); outs() << "\n"; @@ -896,6 +896,7 @@ int main(int argc, char **argv) { && !(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 c1d5ff8..bde72e0 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 // @@ -27,6 +26,7 @@ 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; @@ -43,6 +43,7 @@ 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; diff --git a/tools/llvm-readobj/llvm-readobj.cpp b/tools/llvm-readobj/llvm-readobj.cpp index 32db723..7e6ce49 100644 --- a/tools/llvm-readobj/llvm-readobj.cpp +++ b/tools/llvm-readobj/llvm-readobj.cpp @@ -325,13 +325,12 @@ static void dumpArchive(const Archive *Arc) { static void dumpMachOUniversalBinary(const MachOUniversalBinary *UBinary) { for (const MachOUniversalBinary::ObjectForArch &Obj : UBinary->objects()) { ErrorOr<std::unique_ptr<MachOObjectFile>> ObjOrErr = Obj.getAsObjectFile(); - if (std::error_code EC = ObjOrErr.getError()) { - reportError(UBinary->getFileName(), EC.message()); - continue; - } - - if (MachOObjectFile *MachOObj = ObjOrErr.get().get()) - dumpObject(MachOObj); + 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()); } } diff --git a/tools/llvm-rtdyld/llvm-rtdyld.cpp b/tools/llvm-rtdyld/llvm-rtdyld.cpp index 9462015..480032d 100644 --- a/tools/llvm-rtdyld/llvm-rtdyld.cpp +++ b/tools/llvm-rtdyld/llvm-rtdyld.cpp @@ -13,6 +13,7 @@ #include "llvm/ADT/StringMap.h" #include "llvm/DebugInfo/DWARF/DIContext.h" +#include "llvm/ExecutionEngine/RTDyldMemoryManager.h" #include "llvm/ExecutionEngine/RuntimeDyld.h" #include "llvm/ExecutionEngine/RuntimeDyldChecker.h" #include "llvm/MC/MCAsmInfo.h" @@ -196,7 +197,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. @@ -264,7 +265,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 +288,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); @@ -506,18 +513,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 +548,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); diff --git a/tools/llvm-shlib/CMakeLists.txt b/tools/llvm-shlib/CMakeLists.txt index 08dafe1..bc1b658 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,41 +55,52 @@ 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 PRIVATE ${LIB_NAMES}) diff --git a/tools/opt/BreakpointPrinter.cpp b/tools/opt/BreakpointPrinter.cpp index 3cbc0ae..8f390a1 100644 --- a/tools/opt/BreakpointPrinter.cpp +++ b/tools/opt/BreakpointPrinter.cpp @@ -30,17 +30,15 @@ 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() + "::"; + if (auto *NS = dyn_cast<MDNamespace>(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 (DIType TY = dyn_cast<MDType>(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<MDSubprogram>(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/NewPMDriver.cpp b/tools/opt/NewPMDriver.cpp index 9216d5c..3030d65 100644 --- a/tools/opt/NewPMDriver.cpp +++ b/tools/opt/NewPMDriver.cpp @@ -39,7 +39,9 @@ static cl::opt<bool> bool llvm::runPassPipeline(StringRef Arg0, LLVMContext &Context, Module &M, TargetMachine *TM, tool_output_file *Out, StringRef PassPipeline, OutputKind OK, - VerifierKind VK) { + VerifierKind VK, + bool ShouldPreserveAssemblyUseListOrder, + bool ShouldPreserveBitcodeUseListOrder) { PassBuilder PB(TM); FunctionAnalysisManager FAM(DebugPM); @@ -77,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 5384fe2..349a7b1 100644 --- a/tools/opt/NewPMDriver.h +++ b/tools/opt/NewPMDriver.h @@ -51,7 +51,9 @@ enum VerifierKind { bool runPassPipeline(StringRef Arg0, LLVMContext &Context, Module &M, TargetMachine *TM, tool_output_file *Out, StringRef PassPipeline, opt_tool::OutputKind OK, - opt_tool::VerifierKind VK); + opt_tool::VerifierKind VK, + bool ShouldPreserveAssemblyUseListOrder, + bool ShouldPreserveBitcodeUseListOrder); } #endif diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index c1e120a..80b1934 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -25,6 +25,7 @@ #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" @@ -38,6 +39,7 @@ #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" @@ -178,7 +180,15 @@ 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(legacy::PassManagerBase &PM, Pass *P) { // Add the pass to the pass manager... @@ -265,13 +275,28 @@ static TargetMachine* GetTargetMachine(Triple TheTriple) { // Package up features to be passed to target/subtarget std::string FeaturesStr; - if (MAttrs.size()) { + if (MAttrs.size() || MCPU == "native") { SubtargetFeatures Features; + + // If user asked for the 'native' CPU, we need to autodetect features. + // This is necessary for x86 where the CPU might not support all the + // features the autodetected CPU name lists in the target. For example, + // not all Sandybridge processors support AVX. + if (MCPU == "native") { + StringMap<bool> HostFeatures; + if (sys::getHostCPUFeatures(HostFeatures)) + for (auto &F : HostFeatures) + Features.AddFeature(F.first(), F.second); + } + for (unsigned i = 0; i != MAttrs.size(); ++i) Features.AddFeature(MAttrs[i]); FeaturesStr = Features.getString(); } + if (MCPU == "native") + MCPU = sys::getHostCPUName(); + return TheTarget->createTargetMachine(TheTriple.getTriple(), MCPU, FeaturesStr, InitTargetOptionsFromCodeGenFlags(), @@ -345,6 +370,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)); @@ -396,7 +434,8 @@ int main(int argc, char **argv) { // string. Hand off the rest of the functionality to the new code for that // layer. return runPassPipeline(argv[0], Context, *M, TM.get(), Out.get(), - PassPipeline, OK, VK) + PassPipeline, OK, VK, PreserveAssemblyUseListOrder, + PreserveBitcodeUseListOrder) ? 0 : 1; } @@ -449,10 +488,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 && @@ -524,7 +559,8 @@ int main(int argc, char **argv) { } if (PrintEachXForm) - Passes.add(createPrintModulePass(errs())); + Passes.add( + createPrintModulePass(errs(), "", PreserveAssemblyUseListOrder)); } if (StandardLinkOpts) { @@ -561,9 +597,11 @@ int main(int argc, char **argv) { // 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 ef3d5b3..795d035 100644 --- a/tools/verify-uselistorder/verify-uselistorder.cpp +++ b/tools/verify-uselistorder/verify-uselistorder.cpp @@ -53,7 +53,7 @@ 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>"), @@ -109,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; } @@ -127,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; } @@ -140,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; } @@ -153,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; } @@ -161,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()); @@ -172,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; } @@ -343,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"); @@ -356,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"); @@ -369,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); } @@ -498,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); }); @@ -506,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"); @@ -534,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 6939bc4..61d9851 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> &) { |