From afed9308807d233f0da163b72e613b4a468c5c75 Mon Sep 17 00:00:00 2001 From: Iain Sandoe Date: Sun, 2 Sep 2018 15:12:51 +0100 Subject: [PATCH] [llvm, objc] Add Apple ObjCMetaData library copied from clang-800.0.42.1. --- llvm/include/llvm/ObjCMetadata/ObjCBitcode.h | 122 ++ .../llvm/ObjCMetadata/ObjCMachOBinary.h | 136 ++ llvm/include/llvm/ObjCMetadata/ObjCMetadata.h | 298 ++++ llvm/lib/CMakeLists.txt | 1 + llvm/lib/ObjCMetadata/CMakeLists.txt | 11 + llvm/lib/ObjCMetadata/ObjCBitcode.cpp | 863 +++++++++++ llvm/lib/ObjCMetadata/ObjCMachOBinary.cpp | 1356 +++++++++++++++++ llvm/lib/ObjCMetadata/ObjCMetadata.cpp | 177 +++ llvm/lib/ObjCMetadata/macho-obj.h | 588 +++++++ 9 files changed, 3552 insertions(+) create mode 100644 llvm/include/llvm/ObjCMetadata/ObjCBitcode.h create mode 100644 llvm/include/llvm/ObjCMetadata/ObjCMachOBinary.h create mode 100644 llvm/include/llvm/ObjCMetadata/ObjCMetadata.h create mode 100644 llvm/lib/ObjCMetadata/CMakeLists.txt create mode 100644 llvm/lib/ObjCMetadata/ObjCBitcode.cpp create mode 100644 llvm/lib/ObjCMetadata/ObjCMachOBinary.cpp create mode 100644 llvm/lib/ObjCMetadata/ObjCMetadata.cpp create mode 100644 llvm/lib/ObjCMetadata/macho-obj.h diff --git a/llvm/include/llvm/ObjCMetadata/ObjCBitcode.h b/llvm/include/llvm/ObjCMetadata/ObjCBitcode.h new file mode 100644 index 00000000000..980c874879b --- /dev/null +++ b/llvm/include/llvm/ObjCMetadata/ObjCBitcode.h @@ -0,0 +1,122 @@ +//===- ObjCBitcode.h - ObjC Metadata Bitcode Interface ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares ObjCBitcode interface. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_OBJCMETADATA_OBJCBITCODE_H +#define LLVM_OBJCMETADATA_OBJCBITCODE_H + +#include +#include +#include +#include +#include +#include + +namespace llvm { + +class BitcodeMetadata : public ObjCMetaDataReader { +public: + BitcodeMetadata(Module *M); + + bool isObjC1() const override; + bool isObjC2() const override; + + const Module *getOwningModule() const { + return OwningModule; + } + + // Overrides ObjC Class Ref Implementation. + Expected classes() const override; + Expected getObjCClassFromRef(ObjCClassRef Ref) const override; + bool isObjCClassExternal(ObjCClassRef Ref) const override; + + // Overrides ObjC Category Ref Implementation. + Expected categories() const override; + Expected + getObjCCategoryFromRef(ObjCCategoryRef Ref) const override; + + // Overrides ObjC Protocol Ref Implementation. + Expected protocols() const override; + Expected + getObjCProtocolFromRef(ObjCProtocolRef Ref) const override; + + // Overrides ObjC Class Implementation. + Expected getObjCClassName(ObjCClass ClassRef) const override; + Expected getObjCSuperClassName(ObjCClass Data) const override; + Expected isObjCClassSwift(ObjCClass Data) const override; + Expected isObjCClassMetaClass(ObjCClass Data) const override; + Expected + materializePropertyList(ObjCClass Data) const override; + Expected + materializeInstanceMethodList(ObjCClass Data) const override; + Expected + materializeClassMethodList(ObjCClass Data) const override; + + // Overrides ObjC Category Implementation. + Expected getObjCCategoryName(ObjCCategory Data) const override; + Expected + getObjCCategoryBaseClassName(ObjCCategory Data) const override; + Expected + materializePropertyList(ObjCCategory Data) const override; + Expected + materializeInstanceMethodList(ObjCCategory Data) const override; + Expected + materializeClassMethodList(ObjCCategory Data) const override; + + // Overrides ObjC Protocol Implementation. + Expected getObjCProtocolName(ObjCProtocol Data) const override; + Expected + materializePropertyList(ObjCProtocol Data) const override; + Expected + materializeInstanceMethodList(ObjCProtocol Data) const override; + Expected + materializeClassMethodList(ObjCProtocol Data) const override; + Expected + materializeOptionalInstanceMethodList(ObjCProtocol Data) const override; + Expected + materializeOptionalClassMethodList(ObjCProtocol Data) const override; + + // Override ObjC Property Implementation. + Expected getPropertyName(ObjCProperty Data) const override; + Expected getPropertyAttribute(ObjCProperty Data) const override; + + // Override ObjC Method Implementation. + Expected getMethodName(ObjCMethod Data) const override; + Expected getMethodType(ObjCMethod Data) const override; + + // Override ObjC Selector Ref Implementation. + Expected referencedSelectors() const override; + Expected getObjCSelectorName(ObjCSelectorRef Ref) const override; + + // Override Potentially defined selectors Implemetation. + void getAllPotentiallyDefinedSelectors(StringSet<> &Set) const override; + + // Other. + StringRef getSymbolNameFromRef(ObjCRef Ref) const override; + StringRef guessClassNameBasedOnSymbol(StringRef Sym) const override; + + // Helper function. + bool is64Bit() const { + return Triple(OwningModule->getTargetTriple()).isArch64Bit(); + } + const DataLayout &getDataLayout() const { + return OwningModule->getDataLayout(); + } + Expected getObjC2ClassRO(ObjCClass Data) const; + +private: + Module *OwningModule; +}; + +} + +#endif diff --git a/llvm/include/llvm/ObjCMetadata/ObjCMachOBinary.h b/llvm/include/llvm/ObjCMetadata/ObjCMachOBinary.h new file mode 100644 index 00000000000..3624c84375e --- /dev/null +++ b/llvm/include/llvm/ObjCMetadata/ObjCMachOBinary.h @@ -0,0 +1,136 @@ +//===- ObjCMachOBinary.h - ObjC MachO Metadata Interface ---------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares ObjCMachOBinary interface. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_OBJCMETADATA_OBJCMACHOBINARY_H +#define LLVM_OBJCMETADATA_OBJCMACHOBINARY_H + +#include +#include + +namespace llvm { + +class MachOMetadata : public ObjCMetaDataReader { +public: + MachOMetadata(const object::MachOObjectFile *Binary) + : OwningBinary(Binary) {} + + const object::MachOObjectFile *getMachOObject() { + return cast(OwningBinary); + } + + bool isObjC1() const override; + bool isObjC2() const override; + + const object::MachOObjectFile *getOwningBinary() const { + return OwningBinary; + } + + // Overrides ObjC Class Ref Implementation. + Expected classes() const override; + Expected getObjCClassFromRef(ObjCClassRef Ref) const override; + bool isObjCClassExternal(ObjCClassRef Ref) const override; + + // Overrides ObjC Category Ref Implementation. + Expected categories() const override; + Expected + getObjCCategoryFromRef(ObjCCategoryRef Ref) const override; + + // Overrides ObjC Protocol Ref Implementation. + Expected protocols() const override; + Expected + getObjCProtocolFromRef(ObjCProtocolRef Ref) const override; + + // Overrides ObjC Class Implementation. + Expected getObjCClassName(ObjCClass ClassRef) const override; + Expected getObjCSuperClassName(ObjCClass Data) const override; + Expected isObjCClassSwift(ObjCClass Data) const override; + Expected isObjCClassMetaClass(ObjCClass Data) const override; + Expected + materializePropertyList(ObjCClass Data) const override; + Expected + materializeInstanceMethodList(ObjCClass Data) const override; + Expected + materializeClassMethodList(ObjCClass Data) const override; + + // Overrides ObjC Category Implementation. + Expected getObjCCategoryName(ObjCCategory Data) const override; + Expected + getObjCCategoryBaseClassName(ObjCCategory Data) const override; + Expected + materializePropertyList(ObjCCategory Data) const override; + Expected + materializeInstanceMethodList(ObjCCategory Data) const override; + Expected + materializeClassMethodList(ObjCCategory Data) const override; + + // Overrides ObjC Protocol Implementation. + Expected getObjCProtocolName(ObjCProtocol Data) const override; + Expected + materializePropertyList(ObjCProtocol Data) const override; + Expected + materializeInstanceMethodList(ObjCProtocol Data) const override; + Expected + materializeClassMethodList(ObjCProtocol Data) const override; + Expected + materializeOptionalInstanceMethodList(ObjCProtocol Data) const override; + Expected + materializeOptionalClassMethodList(ObjCProtocol Data) const override; + + // Override ObjC Property Implementation. + Expected getPropertyName(ObjCProperty Data) const override; + Expected getPropertyAttribute(ObjCProperty Data) const override; + + // Override ObjC Method Implementation. + Expected getMethodName(ObjCMethod Data) const override; + Expected getMethodType(ObjCMethod Data) const override; + + // Override ObjC Selector Ref Implementation. + Expected referencedSelectors() const override; + Expected getObjCSelectorName(ObjCSelectorRef Ref) const override; + + // Override Potentially defined selectors Implemetation. + void getAllPotentiallyDefinedSelectors(StringSet<> &Set) const override; + + // Other. + StringRef getSymbolNameFromRef(ObjCRef Ref) const override; + StringRef guessClassNameBasedOnSymbol(StringRef Sym) const override; + + // Helper classes + const object::SectionRef getSection(const char *segname, + const char *sectname) const; + + Expected getValueFromAddress(uint64_t Address, uint64_t size, + bool objc_only = false) const; + Expected getStringFromAddress(uint64_t Address, + bool allowEmpty = false) const; + Expected getSymbolFromAddress(uint64_t Address, + uint64_t &offset) const; + bool isAddressEncrypted(uint64_t Address) const; + // Only available on pointer size. + Expected getResolvedValueFromAddress64(uint64_t Address) const; + Expected getResolvedValueFromAddress32(uint64_t Address) const; + + // ObjC2 helper classes + Expected getObjC2ClassRO64(ObjCClass Data) const; + Expected getObjC2ClassRO32(ObjCClass Data) const; + Expected getObjC2MetaClass64(ObjCClass Data) const; + Expected getObjC2MetaClass32(ObjCClass Data) const; + // ObjC1 helper classes + +private: + const object::MachOObjectFile *OwningBinary; +}; + +} + +#endif diff --git a/llvm/include/llvm/ObjCMetadata/ObjCMetadata.h b/llvm/include/llvm/ObjCMetadata/ObjCMetadata.h new file mode 100644 index 00000000000..b7e1eee9831 --- /dev/null +++ b/llvm/include/llvm/ObjCMetadata/ObjCMetadata.h @@ -0,0 +1,298 @@ +//===- ObjCMetadata.h - Objective C Metadata Interface ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares ObjCMetadata Interfaces. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_OBJCMETADATA_OBJCMETADATA_H +#define LLVM_OBJCMETADATA_OBJCMETADATA_H + +#include +#include +#include +#include +#include +#include +#include + +namespace llvm { + +class ObjCRef { +public: + uint64_t getValue() const { return v.a; } + void setValue(uint64_t value) { v.a = value; } + + Constant *getLLVMConstant() const { return v.c; } + void setLLVMConstant(Constant *C) { v.c = C; } + + int64_t getOffset() const { return offset; } + void setOffset(int64_t bytes) { offset = bytes; } + + ObjCRef() { + memset(&v, 0, sizeof(v)); + } + ObjCRef(uint64_t value) : offset (0) { + v.a = value; + } + ObjCRef(Constant *C) : offset (0) { + v.c = C; + } + ObjCRef(Constant *C, int64_t offset) : offset(offset) { + v.c = C; + } + + bool operator==(const ObjCRef &Other) const { + return std::memcmp(&v, &(Other.v), sizeof(v)) == 0; + } + + bool operator!=(const ObjCRef &Other) const { + return !(*this == Other); + } + +private: + union { + uint64_t a; + Constant *c; + } v; + int64_t offset; // only used with v is Constant. FIXME: this is a hack. +}; + +class ObjCMetaDataReader; +class ObjCClassRef; +class ObjCProtocolRef; +class ObjCCategoryRef; +class ObjCMethod; +class ObjCProperty; +class ObjCSelectorRef; + +typedef SmallVector ObjCMethodList; +typedef SmallVector ObjCPropertyList; +typedef SmallVector ObjCClassList; +typedef SmallVector ObjCCategoryList; +typedef SmallVector ObjCProtocolList; +typedef SmallVector ObjCSelectorList; + +class ObjCInfoBase { +public: + ObjCInfoBase(const ObjCMetaDataReader *Reader) + : Data(), MetadataReader(Reader) {} + ObjCInfoBase(const ObjCMetaDataReader *Reader, ObjCRef Data) + : Data(Data), MetadataReader(Reader) {} + + bool operator==(const ObjCInfoBase &Other) const { + return Data == Other.Data; + } + + ObjCRef &getRawContent() { return Data; } + +protected: + ObjCRef Data; + const ObjCMetaDataReader *MetadataReader; +}; + +class ObjCProperty : public ObjCInfoBase { +public: + ObjCProperty(const ObjCMetaDataReader *Reader) : ObjCInfoBase(Reader) {} + ObjCProperty(const ObjCMetaDataReader *Reader, ObjCRef Data) + : ObjCInfoBase(Reader, Data) {} + + Expected getName() const; + Expected getAttribute() const; + + // Query for setter and getter. + // Return empty string if doesn't exists. + Expected getGetter() const; + Expected getSetter() const; +}; + +class ObjCMethod : public ObjCInfoBase { +public: + ObjCMethod(const ObjCMetaDataReader *Reader) : ObjCInfoBase(Reader) {} + ObjCMethod(const ObjCMetaDataReader *Reader, ObjCRef Data) + : ObjCInfoBase(Reader, Data) {} + + Expected getName() const; + Expected getType() const; +}; + +// Interface to read the ObjCClass. +class ObjCClass : public ObjCInfoBase { +public: + ObjCClass(const ObjCMetaDataReader *Reader) : ObjCInfoBase(Reader) {} + ObjCClass(const ObjCMetaDataReader *Reader, ObjCRef Data) + : ObjCInfoBase(Reader, Data) {} + + Expected getName() const; + Expected getSuperClassName() const; + + Expected isSwift() const; + Expected isMetaClass() const; + + Expected properties() const; + Expected instanceMethods() const; + Expected classMethods() const; +}; + +class ObjCCategory : public ObjCInfoBase { +public: + ObjCCategory(const ObjCMetaDataReader *Reader) : ObjCInfoBase(Reader) {} + ObjCCategory(const ObjCMetaDataReader *Reader, ObjCRef Data) + : ObjCInfoBase(Reader, Data) {} + + Expected getName() const; + Expected getBaseClassName() const; + + Expected properties() const; + Expected instanceMethods() const; + Expected classMethods() const; +}; + +class ObjCProtocol : public ObjCInfoBase { +public: + ObjCProtocol(const ObjCMetaDataReader *Reader) : ObjCInfoBase(Reader) {} + ObjCProtocol(const ObjCMetaDataReader *Reader, ObjCRef Data) + : ObjCInfoBase(Reader, Data) {} + + Expected getName() const; + + Expected properties() const; + Expected instanceMethods() const; + Expected classMethods() const; + Expected optionalInstanceMethods() const; + Expected optionalClassMethods() const; +}; + +class ObjCClassRef : public ObjCInfoBase { +public: + ObjCClassRef(const ObjCMetaDataReader *Reader) : ObjCInfoBase(Reader) {} + ObjCClassRef(const ObjCMetaDataReader *Reader, ObjCRef Data) + : ObjCInfoBase(Reader, Data) {} + + Expected getClassName() const; + bool isExternal() const; + Expected getObjCClass() const; + Expected operator*() const { return getObjCClass(); } +}; + +class ObjCCategoryRef : public ObjCInfoBase { +public: + ObjCCategoryRef(const ObjCMetaDataReader *Reader) : ObjCInfoBase(Reader) {} + ObjCCategoryRef(const ObjCMetaDataReader *Reader, ObjCRef Data) + : ObjCInfoBase(Reader, Data) {} + + Expected getObjCCategory() const; + Expected operator*() const { return getObjCCategory(); } +}; + +class ObjCProtocolRef : public ObjCInfoBase { +public: + ObjCProtocolRef(const ObjCMetaDataReader *Reader) : ObjCInfoBase(Reader) {} + ObjCProtocolRef(const ObjCMetaDataReader *Reader, ObjCRef Data) + : ObjCInfoBase(Reader, Data) {} + + Expected getObjCProtocol() const; + Expected operator*() const { return getObjCProtocol(); } +}; + +class ObjCSelectorRef : public ObjCInfoBase { +public: + ObjCSelectorRef(const ObjCMetaDataReader *Reader) : ObjCInfoBase(Reader) {} + ObjCSelectorRef(const ObjCMetaDataReader *Reader, ObjCRef Data) + : ObjCInfoBase(Reader, Data) {} + + Expected getSelector() const; +}; + +// ObjC Metadata Reader. +class ObjCMetaDataReader { +public: + virtual ~ObjCMetaDataReader() {} + + // General + virtual bool isObjC1() const = 0; + virtual bool isObjC2() const = 0; + + // ObjCClassRef. + virtual Expected classes() const = 0; + virtual Expected getObjCClassFromRef(ObjCClassRef Ref) const = 0; + virtual bool isObjCClassExternal(ObjCClassRef Ref) const = 0; + + // ObjCCategoryRef. + virtual Expected categories() const = 0; + virtual Expected + getObjCCategoryFromRef(ObjCCategoryRef Ref) const = 0; + + // ObjCProtocolRef. + virtual Expected protocols() const = 0; + virtual Expected + getObjCProtocolFromRef(ObjCProtocolRef Ref) const = 0; + + // ObjCClass. + virtual Expected getObjCClassName(ObjCClass Data) const = 0; + virtual Expected + getObjCSuperClassName(ObjCClass Data) const = 0; + virtual Expected isObjCClassSwift(ObjCClass Data) const = 0; + virtual Expected isObjCClassMetaClass(ObjCClass Data) const = 0; + virtual Expected + materializePropertyList(ObjCClass Data) const = 0; + virtual Expected + materializeInstanceMethodList(ObjCClass Data) const = 0; + virtual Expected + materializeClassMethodList(ObjCClass Data) const = 0; + + // ObjCCategory. + virtual Expected getObjCCategoryName(ObjCCategory Data) const = 0; + virtual Expected + getObjCCategoryBaseClassName(ObjCCategory Data) const = 0; + virtual Expected + materializePropertyList(ObjCCategory Data) const = 0; + virtual Expected + materializeInstanceMethodList(ObjCCategory Data) const = 0; + virtual Expected + materializeClassMethodList(ObjCCategory Data) const = 0; + + // ObjCProtocol. + virtual Expected getObjCProtocolName(ObjCProtocol Data) const = 0; + virtual Expected + materializePropertyList(ObjCProtocol Data) const = 0; + virtual Expected + materializeInstanceMethodList(ObjCProtocol Data) const = 0; + virtual Expected + materializeClassMethodList(ObjCProtocol Data) const = 0; + virtual Expected + materializeOptionalInstanceMethodList(ObjCProtocol Data) const = 0; + virtual Expected + materializeOptionalClassMethodList(ObjCProtocol Data) const = 0; + + // ObjCProperty. + virtual Expected getPropertyName(ObjCProperty Data) const = 0; + virtual Expected + getPropertyAttribute(ObjCProperty Data) const = 0; + + // ObjCMethod. + virtual Expected getMethodName(ObjCMethod Data) const = 0; + virtual Expected getMethodType(ObjCMethod Data) const = 0; + + // ObjCSelectorRef. + virtual Expected referencedSelectors() const = 0; + virtual Expected + getObjCSelectorName(ObjCSelectorRef Ref) const = 0; + + // Potentially defined selectors. + virtual void getAllPotentiallyDefinedSelectors(StringSet<> &Set) const = 0; + + // Other. + virtual StringRef getSymbolNameFromRef(ObjCRef Ref) const = 0; + virtual StringRef guessClassNameBasedOnSymbol(StringRef Sym) const = 0; +}; + +} + +#endif diff --git a/llvm/lib/CMakeLists.txt b/llvm/lib/CMakeLists.txt index ecf8b93d253..5ed501e0c90 100644 --- a/llvm/lib/CMakeLists.txt +++ b/llvm/lib/CMakeLists.txt @@ -14,6 +14,7 @@ add_subdirectory(LTO) add_subdirectory(MC) add_subdirectory(Object) add_subdirectory(ObjectYAML) +add_subdirectory(ObjCMetadata) add_subdirectory(Option) add_subdirectory(DebugInfo) add_subdirectory(ExecutionEngine) diff --git a/llvm/lib/ObjCMetadata/CMakeLists.txt b/llvm/lib/ObjCMetadata/CMakeLists.txt new file mode 100644 index 00000000000..81cd8f2947b --- /dev/null +++ b/llvm/lib/ObjCMetadata/CMakeLists.txt @@ -0,0 +1,11 @@ +add_llvm_library(LLVMObjCMetadata + ObjCBitcode.cpp + ObjCMetadata.cpp + ObjCMachOBinary.cpp + + ADDITIONAL_HEADER_DIRS + ${LLVM_MAIN_INCLUDE_DIR}/llvm/ObjCMetadata + + DEPENDS + intrinsics_gen + ) diff --git a/llvm/lib/ObjCMetadata/ObjCBitcode.cpp b/llvm/lib/ObjCMetadata/ObjCBitcode.cpp new file mode 100644 index 00000000000..d8026a10b74 --- /dev/null +++ b/llvm/lib/ObjCMetadata/ObjCBitcode.cpp @@ -0,0 +1,863 @@ +//===- ObjCBitcode.cpp - Bitcode ObjC Metadata Reader -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the classes needed to parse ObjCMetadata in Bitcode. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ObjCMetadata/ObjCBitcode.h" +#include "llvm/ObjCMetadata/ObjCMetadata.h" +#include "llvm/Analysis/InstructionSimplify.h" +#include "llvm/Analysis/ValueTracking.h" +#include "llvm/IR/Constant.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/GlobalAlias.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/Operator.h" +#include "llvm/Support/Error.h" + +#include "macho-obj.h" + +using namespace llvm; +using namespace object; + +static Value *GetValueAtStructOffset(ConstantStruct *CS, uint64_t offset, + const DataLayout &DL) { + StructType* ST = CS->getType(); + unsigned index = DL.getStructLayout(ST)->getElementContainingOffset(offset); + Value* Ret = CS->getOperand(index); + offset -= DL.getStructLayout(ST)->getElementOffset(index); + if (offset != 0) { + CS = dyn_cast(Ret); + ST = CS->getType(); + return GetValueAtStructOffset(CS, offset, DL); + } + return Ret; +} + +Value *GetRealValue(Value *GV, const DataLayout &DL) { + int64_t offset = 0; + auto basePtr = dyn_cast( + GetPointerBaseWithConstantOffset(GV, offset, DL)); + if (!basePtr || !basePtr->hasInitializer()) { + return nullptr; + } + if (offset == 0) + return basePtr->getInitializer(); + else if (auto CS = dyn_cast(basePtr->getInitializer())) + return GetValueAtStructOffset(CS, offset, DL); + else + return nullptr; +} + +static User *GlobalOpToUser(Value *v) { + auto base_user = dyn_cast(v); + assert(base_user && "Provided value is not a user!"); + auto op0 = dyn_cast(base_user->getOperand(0)); + assert(op0 && "User doesn't have User operands!"); + return op0; +} + +// This is basically the GetUnderlyingObject in ValueTracking Analysis, +// except it works through non-pointer types. +static Value *GetUnderlyingValue(Value *V, const DataLayout &DL, + unsigned MaxLookup) { + for (unsigned Count = 0; MaxLookup == 0 || Count < MaxLookup; ++Count) { + if (GEPOperator *GEP = dyn_cast(V)) { + V = GEP->getPointerOperand(); + } else if (Operator::getOpcode(V) == Instruction::BitCast || + Operator::getOpcode(V) == Instruction::AddrSpaceCast) { + V = cast(V)->getOperand(0); + } else if (GlobalAlias *GA = dyn_cast(V)) { + if (!GA->isDefinitionExact()) + return V; + V = GA->getAliasee(); + } else if (PtrToIntOperator *PTI = dyn_cast(V)) { + V = PTI->getPointerOperand(); + } else { + // See if InstructionSimplify knows any relevant tricks. + if (Instruction *I = dyn_cast(V)) + // TODO: Acquire a DominatorTree and AssumptionCache and use them. + if (Value *Simplified = SimplifyInstruction(I, DL, nullptr)) { + V = Simplified; + continue; + } + + return V; + } + } + return V; +} + +// Check if a selector is actually defined with corresponding method, +// even though it is not showing up when walking through the objc class +// info. For example, this will find the selector defined in @objc swift +// generic class. +static bool hasUserInMethodList(const Value *V, const Module *M, + unsigned maxDepth = 4) { + // Search through all the uses of the selector string to see if there are any + // usage in the __DATA,__objc_const section that looks like a method entry. + for (const auto &user : V->users()) { + if (auto *GV = dyn_cast(user)) { + if (GV->hasSection()) { + StringRef Section = GV->getSection(); + std::string SectionName; + // Trim all spaces in the section name. + for (auto c : Section) { + if (c != ' ') + SectionName.push_back(c); + } + // Potential match of a method list. + // The method list should have type {i32, i32, [n x {i8*, i8*, i8*}]}. + if (StringRef(SectionName).startswith("__DATA,__objc_const") && + V->getType()->isStructTy() && + V->getType()->getStructNumElements() == 3 && + V->getType()->getStructElementType(0)->isIntegerTy() && + V->getType()->getStructElementType(1)->isIntegerTy() && + V->getType()->getStructElementType(2)->isArrayTy()) + return true; + } + } + // Recursively find method list. + if (maxDepth != 0 && hasUserInMethodList(user, M, maxDepth - 1)) + return true; + } + return false; +} + +BitcodeMetadata::BitcodeMetadata(Module *M) : OwningModule(M) {} + +Expected BitcodeMetadata::classes() const { + ObjCClassList ClassList; + for (auto &GV : OwningModule->globals()) { + if (GV.hasSection()) { + StringRef Section = GV.getSection(); + std::string SectionName; + // Trim all spaces in the section name. + for (auto c : Section) { + if (c != ' ') + SectionName.push_back(c); + } + if (SectionName == "__DATA,__objc_classlist,regular,no_dead_strip") { + auto structList = dyn_cast( + GetUnderlyingObject(GV.getInitializer(), getDataLayout(), 2)); + for (const auto &op : structList->operands()) + ClassList.push_back(ObjCClassRef(this, cast(&op))); + } + } + } + + return ClassList; +} + +Expected +BitcodeMetadata::getObjCClassFromRef(ObjCClassRef Ref) const { + auto GV = Ref.getRawContent().getLLVMConstant(); + int64_t class_offset = 0; + auto objc_class = dyn_cast( + GetPointerBaseWithConstantOffset(GV, class_offset, getDataLayout())); + if (!objc_class) + return make_error("Unable to find class associsated", + object_error::parse_failed); + + return ObjCClass(this, {objc_class, class_offset}); +} + +bool BitcodeMetadata::isObjCClassExternal(ObjCClassRef Ref) const { + if (auto GV = cast(Ref.getRawContent().getLLVMConstant())) { + if (GV->hasInitializer()) + return false; + } + return true; +} + +Expected BitcodeMetadata::categories() const { + ObjCCategoryList CategoryList; + for (auto &GV : OwningModule->globals()) { + if (GV.hasSection()) { + StringRef Section = GV.getSection(); + std::string SectionName; + // Trim all spaces in the section name. + for (auto c : Section) { + if (c != ' ') + SectionName.push_back(c); + } + if (SectionName == "__DATA,__objc_catlist,regular,no_dead_strip") { + auto structList = dyn_cast( + GetUnderlyingObject(GV.getInitializer(), getDataLayout(), 2)); + for (const auto &op : structList->operands()) + CategoryList.push_back(ObjCCategoryRef(this, cast(&op))); + } + } + } + return CategoryList; +} + +Expected +BitcodeMetadata::getObjCCategoryFromRef(ObjCCategoryRef Ref) const { + auto GV = Ref.getRawContent().getLLVMConstant(); + int64_t cat_offset = 0; + auto objc_category = dyn_cast( + GetPointerBaseWithConstantOffset(GV, cat_offset, getDataLayout())); + if (!objc_category) + return make_error("Unable to find category associsated", + object_error::parse_failed); + + return ObjCCategory(this, {objc_category, cat_offset}); +} + +Expected BitcodeMetadata::protocols() const { + ObjCProtocolList ProtocolList; + for (auto &GV : OwningModule->globals()) { + if (GV.hasSection()) { + StringRef Section = GV.getSection(); + std::string SectionName; + // Trim all spaces in the section name. + for (auto c : Section) { + if (c != ' ') + SectionName.push_back(c); + } + if (SectionName == "__DATA,__objc_protolist,coalesced,no_dead_strip") { + ProtocolList.push_back(ObjCProtocolRef(this, &GV)); + } + } + } + return ProtocolList; +} + +Expected +BitcodeMetadata::getObjCProtocolFromRef(ObjCProtocolRef Ref) const { + auto GV = cast(Ref.getRawContent().getLLVMConstant()); + auto protocol = dyn_cast( + GetUnderlyingObject(GV->getInitializer(), getDataLayout(), 2)); + if (!protocol) + return make_error("Unable to find protocol associsated", + object_error::parse_failed); + + return ObjCProtocol(this, protocol); +} + +Expected +BitcodeMetadata::getObjCClassName(ObjCClass ClassRef) const { + if (auto ro = getObjC2ClassRO(ClassRef)) { + unsigned offset = + is64Bit() ? offsetof(class_ro64_t, name) : offsetof(class_ro32_t, name); + auto class_name_global = dyn_cast(GetUnderlyingObject( + GetValueAtStructOffset(*ro, offset, getDataLayout()), getDataLayout(), + 2)); + if (!class_name_global) + return make_error("Could not read ObjC class name", + object_error::parse_failed); + ConstantDataSequential *CD = + dyn_cast(class_name_global->getInitializer()); + if (!CD) + return make_error( + "Class name must be a ConstantDataSequential", + object_error::parse_failed); + return CD->getAsString().rtrim('\0'); + } else + return ro.takeError(); +} + +Expected +BitcodeMetadata::getObjCSuperClassName(ObjCClass Data) const { + auto objc_class = + cast(Data.getRawContent().getLLVMConstant()); + auto objc_class_t = cast( + GlobalOpToUser(GetUnderlyingValue(objc_class, getDataLayout(), 2))); + int64_t class_offset = Data.getRawContent().getOffset(); + // read class_ro + unsigned offset = is64Bit() ? offsetof(class64_t, superclass) + : offsetof(class32_t, superclass); + auto superclass_t = GetValueAtStructOffset( + objc_class_t, class_offset + offset, getDataLayout()); + if (!isa(superclass_t)) { + if (auto superclass_global = dyn_cast(superclass_t)) { + auto superclassref = ObjCClassRef(this, superclass_global); + return superclassref.getClassName(); + } else + return make_error("Cannot process superclass", + object_error::parse_failed); + } + return StringRef(); +} + +Expected BitcodeMetadata::isObjCClassSwift(ObjCClass Data) const { + ConstantStruct *objc_class_t = nullptr; + if (auto objc_class = + dyn_cast(Data.getRawContent().getLLVMConstant())) { + objc_class_t = cast( + GlobalOpToUser(GetUnderlyingValue(objc_class, getDataLayout(), 2))); + } else if (auto objc_class = dyn_cast( + Data.getRawContent().getLLVMConstant())) + objc_class_t = objc_class; + else + return make_error("Unexpected type for objc class", + object_error::parse_failed); + + int64_t class_offset = Data.getRawContent().getOffset(); + // read class_ro + unsigned offset = + is64Bit() ? offsetof(class64_t, data) : offsetof(class32_t, data); + auto class_ro_v = GetValueAtStructOffset(objc_class_t, class_offset + offset, + getDataLayout()); + if (isa(class_ro_v)) { + // swift generates class_ro with an offset using add constant expr + return true; + } + return false; +} + +Expected BitcodeMetadata::isObjCClassMetaClass(ObjCClass Data) const { + if (auto ro = getObjC2ClassRO(Data)) { + unsigned offset = is64Bit() ? offsetof(class_ro64_t, flags) + : offsetof(class_ro32_t, flags); + auto ClassFlag = dyn_cast( + GetUnderlyingValue(GetValueAtStructOffset(*ro, offset, getDataLayout()), + getDataLayout(), 2)); + if (!ClassFlag) + return make_error("Could not get flags from objc class", + object_error::parse_failed); + return ((ClassFlag->getZExtValue() & RO_META) != 0); + } else + return ro.takeError(); +} + +Expected +BitcodeMetadata::materializePropertyList(ObjCClass ClassRef) const { + ObjCPropertyList PropertyList; + if (auto ro = getObjC2ClassRO(ClassRef)) { + unsigned offset = is64Bit() ? offsetof(class_ro64_t, baseProperties) + : offsetof(class_ro32_t, baseProperties); + auto objc_prop_list = dyn_cast( + GetUnderlyingValue(GetValueAtStructOffset(*ro, offset, getDataLayout()), + getDataLayout(), 2)); + if (objc_prop_list && objc_prop_list->hasInitializer()) { + auto property_list_t = GlobalOpToUser(objc_prop_list); + auto property_list = cast(property_list_t->getOperand(2)); + for (auto &op : property_list->operands()) + PropertyList.push_back(ObjCProperty(this, cast(&op))); + } + return PropertyList; + } else + return ro.takeError(); +} + +Expected BitcodeMetadata::getPropertyName(ObjCProperty Data) const { + auto NameVar = dyn_cast( + GetUnderlyingValue(Data.getRawContent().getLLVMConstant()->getOperand(0), + getDataLayout(), 2)); + if (!NameVar) + return make_error("Could not find property name", + object_error::parse_failed); + + ConstantDataSequential *CD = + dyn_cast(NameVar->getInitializer()); + if (!CD) + return make_error("Could not parse property name", + object_error::parse_failed); + + return CD->getAsString().rtrim('\0'); +} + +Expected +BitcodeMetadata::getPropertyAttribute(ObjCProperty Data) const { + auto AttrVar = dyn_cast( + GetUnderlyingValue(Data.getRawContent().getLLVMConstant()->getOperand(1), + getDataLayout(), 2)); + if (!AttrVar) + return make_error("Could not find property attr", + object_error::parse_failed); + + ConstantDataSequential *CD = + dyn_cast(AttrVar->getInitializer()); + if (!CD) + return make_error("Could not parse property attr", + object_error::parse_failed); + + return CD->getAsString().rtrim('\0'); +} + +Expected +BitcodeMetadata::materializeInstanceMethodList(ObjCClass ClassRef) const { + ObjCMethodList MethodList; + if (auto ro = getObjC2ClassRO(ClassRef)) { + unsigned offset = is64Bit() ? offsetof(class_ro64_t, baseMethods) + : offsetof(class_ro32_t, baseMethods); + auto objc_method_list = dyn_cast( + GetUnderlyingValue(GetValueAtStructOffset(*ro, offset, getDataLayout()), + getDataLayout(), 2)); + if (objc_method_list->getNumOperands() != 0) { + auto methList = + cast(GlobalOpToUser(objc_method_list)->getOperand(2)); + for (auto &op : methList->operands()) + MethodList.push_back(ObjCMethod(this, cast(&op))); + } + return MethodList; + } else + return ro.takeError(); +} + +Expected +BitcodeMetadata::materializeClassMethodList(ObjCClass ClassRef) const { + ConstantStruct *objc_class_t = nullptr; + if (auto objc_class = dyn_cast( + ClassRef.getRawContent().getLLVMConstant())) { + objc_class_t = cast( + GlobalOpToUser(GetUnderlyingValue(objc_class, getDataLayout(), 2))); + } else + return make_error("Unexpected type for objc meta class", + object_error::parse_failed); + + int64_t class_offset = ClassRef.getRawContent().getOffset(); + auto metaclass = + GetValueAtStructOffset(objc_class_t, class_offset, getDataLayout()); + int64_t metaclass_offset = 0; + auto metaclass_g = dyn_cast(GetPointerBaseWithConstantOffset( + metaclass, metaclass_offset, getDataLayout())); + + if (!metaclass_g) + return make_error("Unable to find meta class associsated", + object_error::parse_failed); + + auto MetaClass = ObjCClass(this, {metaclass_g, metaclass_offset}); + return materializeInstanceMethodList(MetaClass); +} + +Expected +BitcodeMetadata::getObjCCategoryName(ObjCCategory Cat) const { + ConstantStruct *objc_cat_t = nullptr; + if (auto objc_category = + dyn_cast(Cat.getRawContent().getLLVMConstant())) { + objc_cat_t = cast( + GlobalOpToUser(GetUnderlyingValue(objc_category, getDataLayout(), 2))); + } else + return make_error("Unexpected type for objc category", + object_error::parse_failed); + + int64_t cat_offset = Cat.getRawContent().getOffset(); + unsigned offset = + is64Bit() ? offsetof(category64_t, name) : offsetof(category32_t, name); + auto catname = dyn_cast(GetUnderlyingObject( + GetValueAtStructOffset(objc_cat_t, cat_offset + offset, getDataLayout()), + getDataLayout(), 2)); + if (!catname) + return make_error("Cannot extract catogery name", + object_error::parse_failed); + + ConstantDataSequential *CD = + dyn_cast(catname->getInitializer()); + if (!CD) + return make_error( + "Category name must be a ConstantDataSequential", + object_error::parse_failed); + return CD->getAsString().rtrim('\0'); +} + +Expected +BitcodeMetadata::getObjCCategoryBaseClassName(ObjCCategory Cat) const { + ConstantStruct *objc_cat_t = nullptr; + if (auto objc_category = + dyn_cast(Cat.getRawContent().getLLVMConstant())) { + objc_cat_t = cast( + GlobalOpToUser(GetUnderlyingValue(objc_category, getDataLayout(), 2))); + } else + return make_error("Unexpected type for objc category", + object_error::parse_failed); + + int64_t cat_offset = Cat.getRawContent().getOffset(); + unsigned offset = + is64Bit() ? offsetof(category64_t, cls) : offsetof(category32_t, cls); + auto baseCls = dyn_cast(GetUnderlyingObject( + GetValueAtStructOffset(objc_cat_t, cat_offset + offset, getDataLayout()), + getDataLayout(), 2)); + if (!baseCls) + return make_error("Cannot extract category base class", + object_error::parse_failed); + auto baseClsRef = ObjCClassRef(this, baseCls); + return baseClsRef.getClassName(); +} + +Expected +BitcodeMetadata::materializePropertyList(ObjCCategory Cat) const { + ConstantStruct *objc_cat_t = nullptr; + if (auto objc_category = + dyn_cast(Cat.getRawContent().getLLVMConstant())) { + objc_cat_t = cast( + GlobalOpToUser(GetUnderlyingValue(objc_category, getDataLayout(), 2))); + } else + return make_error("Unexpected type for objc category", + object_error::parse_failed); + + ObjCPropertyList PropertyList; + int64_t cat_offset = Cat.getRawContent().getOffset(); + unsigned offset = is64Bit() ? offsetof(category64_t, instanceProperties) + : offsetof(category32_t, instanceProperties); + auto props = dyn_cast(GetUnderlyingObject( + GetValueAtStructOffset(objc_cat_t, cat_offset + offset, getDataLayout()), + getDataLayout(), 2)); + + if (props && props->hasInitializer()) { + auto property_list_t = GlobalOpToUser(props); + auto property_list = cast(property_list_t->getOperand(2)); + for (auto &op : property_list->operands()) + PropertyList.push_back(ObjCProperty(this, cast(&op))); + } + return PropertyList; +} + +Expected +BitcodeMetadata::materializeInstanceMethodList(ObjCCategory Cat) const { + ConstantStruct *objc_cat_t = nullptr; + if (auto objc_category = + dyn_cast(Cat.getRawContent().getLLVMConstant())) { + objc_cat_t = cast( + GlobalOpToUser(GetUnderlyingValue(objc_category, getDataLayout(), 2))); + } else + return make_error("Unexpected type for objc category", + object_error::parse_failed); + + ObjCMethodList MethodList; + int64_t cat_offset = Cat.getRawContent().getOffset(); + unsigned offset = is64Bit() ? offsetof(category64_t, instanceMethods) + : offsetof(category32_t, instanceMethods); + auto method_list_global = dyn_cast(GetUnderlyingObject( + GetValueAtStructOffset(objc_cat_t, cat_offset + offset, getDataLayout()), + getDataLayout(), 2)); + if (method_list_global->getNumOperands() != 0) { + auto methList = + dyn_cast(GlobalOpToUser(method_list_global)->getOperand(2)); + for (auto &op : methList->operands()) + MethodList.push_back(ObjCMethod(this, cast(&op))); + } + return MethodList; +} + +Expected +BitcodeMetadata::materializeClassMethodList(ObjCCategory Cat) const { + ConstantStruct *objc_cat_t = nullptr; + if (auto objc_category = + dyn_cast(Cat.getRawContent().getLLVMConstant())) { + objc_cat_t = cast( + GlobalOpToUser(GetUnderlyingValue(objc_category, getDataLayout(), 2))); + } else + return make_error("Unexpected type for objc category", + object_error::parse_failed); + + ObjCMethodList MethodList; + int64_t cat_offset = Cat.getRawContent().getOffset(); + unsigned offset = is64Bit() ? offsetof(category64_t, classMethods) + : offsetof(category32_t, classMethods); + auto method_list_global = dyn_cast(GetUnderlyingObject( + GetValueAtStructOffset(objc_cat_t, cat_offset + offset, getDataLayout()), + getDataLayout(), 2)); + if (method_list_global->getNumOperands() != 0) { + auto methList = + dyn_cast(GlobalOpToUser(method_list_global)->getOperand(2)); + for (auto &op : methList->operands()) + MethodList.push_back(ObjCMethod(this, cast(&op))); + } + return MethodList; +} + +Expected +BitcodeMetadata::getObjCProtocolName(ObjCProtocol Data) const { + auto *objc_proto_t = cast(GlobalOpToUser(GetUnderlyingValue( + Data.getRawContent().getLLVMConstant(), getDataLayout(), 2))); + if (!objc_proto_t) + return make_error("Unexpected type for objc protocol", + object_error::parse_failed); + + unsigned offset = + is64Bit() ? offsetof(protocol64_t, name) : offsetof(protocol32_t, name); + auto protoName = dyn_cast(GetUnderlyingObject( + GetValueAtStructOffset(objc_proto_t, offset, getDataLayout()), + getDataLayout(), 2)); + if (!protoName) + return make_error("Cannot extract protocol name", + object_error::parse_failed); + + ConstantDataSequential *CD = + dyn_cast(protoName->getInitializer()); + if (!CD) + return make_error( + "Protocol name must be a ConstantDataSequential", + object_error::parse_failed); + return CD->getAsString().rtrim('\0'); +} + +Expected +BitcodeMetadata::materializePropertyList(ObjCProtocol Data) const { + auto *objc_proto_t = cast(GlobalOpToUser(GetUnderlyingValue( + Data.getRawContent().getLLVMConstant(), getDataLayout(), 2))); + if (!objc_proto_t) + return make_error("Unexpected type for objc protocol", + object_error::parse_failed); + + unsigned offset = is64Bit() ? offsetof(protocol64_t, instanceProperties) + : offsetof(protocol32_t, instanceProperties); + + ObjCPropertyList PropertyList; + auto props = dyn_cast(GetUnderlyingObject( + GetValueAtStructOffset(objc_proto_t, offset, getDataLayout()), + getDataLayout(), 2)); + + if (props && props->hasInitializer()) { + auto property_list_t = GlobalOpToUser(props); + auto property_list = cast(property_list_t->getOperand(2)); + for (auto &op : property_list->operands()) + PropertyList.push_back(ObjCProperty(this, cast(&op))); + } + return PropertyList; +} + +Expected +BitcodeMetadata::materializeInstanceMethodList(ObjCProtocol Data) const { + auto *objc_proto_t = cast(GlobalOpToUser(GetUnderlyingValue( + Data.getRawContent().getLLVMConstant(), getDataLayout(), 2))); + if (!objc_proto_t) + return make_error("Unexpected type for objc protocol", + object_error::parse_failed); + + unsigned offset = is64Bit() ? offsetof(protocol64_t, instanceMethods) + : offsetof(protocol32_t, instanceMethods); + + ObjCMethodList MethodList; + auto method_list_global = dyn_cast(GetUnderlyingObject( + GetValueAtStructOffset(objc_proto_t, offset, getDataLayout()), + getDataLayout(), 2)); + if (method_list_global->getNumOperands() != 0) { + auto methList = + dyn_cast(GlobalOpToUser(method_list_global)->getOperand(2)); + for (auto &op : methList->operands()) + MethodList.push_back(ObjCMethod(this, cast(&op))); + } + return MethodList; +} + +Expected +BitcodeMetadata::materializeClassMethodList(ObjCProtocol Data) const { + auto *objc_proto_t = cast(GlobalOpToUser(GetUnderlyingValue( + Data.getRawContent().getLLVMConstant(), getDataLayout(), 2))); + if (!objc_proto_t) + return make_error("Unexpected type for objc protocol", + object_error::parse_failed); + + unsigned offset = is64Bit() ? offsetof(protocol64_t, classMethods) + : offsetof(protocol32_t, classMethods); + + ObjCMethodList MethodList; + auto method_list_global = dyn_cast(GetUnderlyingObject( + GetValueAtStructOffset(objc_proto_t, offset, getDataLayout()), + getDataLayout(), 2)); + if (method_list_global->getNumOperands() != 0) { + auto methList = + dyn_cast(GlobalOpToUser(method_list_global)->getOperand(2)); + for (auto &op : methList->operands()) + MethodList.push_back(ObjCMethod(this, cast(&op))); + } + return MethodList; +} + +Expected BitcodeMetadata::materializeOptionalInstanceMethodList( + ObjCProtocol Data) const { + auto *objc_proto_t = cast(GlobalOpToUser(GetUnderlyingValue( + Data.getRawContent().getLLVMConstant(), getDataLayout(), 2))); + if (!objc_proto_t) + return make_error("Unexpected type for objc protocol", + object_error::parse_failed); + + unsigned offset = is64Bit() ? offsetof(protocol64_t, optionalInstanceMethods) + : offsetof(protocol32_t, optionalInstanceMethods); + + ObjCMethodList MethodList; + auto method_list_global = dyn_cast(GetUnderlyingObject( + GetValueAtStructOffset(objc_proto_t, offset, getDataLayout()), + getDataLayout(), 2)); + if (method_list_global->getNumOperands() != 0) { + auto methList = + dyn_cast(GlobalOpToUser(method_list_global)->getOperand(2)); + for (auto &op : methList->operands()) + MethodList.push_back(ObjCMethod(this, cast(&op))); + } + return MethodList; +} + +Expected +BitcodeMetadata::materializeOptionalClassMethodList(ObjCProtocol Data) const { + auto *objc_proto_t = cast(GlobalOpToUser(GetUnderlyingValue( + Data.getRawContent().getLLVMConstant(), getDataLayout(), 2))); + if (!objc_proto_t) + return make_error("Unexpected type for objc protocol", + object_error::parse_failed); + + unsigned offset = is64Bit() ? offsetof(protocol64_t, optionalClassMethods) + : offsetof(protocol32_t, optionalClassMethods); + + ObjCMethodList MethodList; + auto method_list_global = dyn_cast(GetUnderlyingObject( + GetValueAtStructOffset(objc_proto_t, offset, getDataLayout()), + getDataLayout(), 2)); + if (method_list_global->getNumOperands() != 0) { + auto methList = + dyn_cast(GlobalOpToUser(method_list_global)->getOperand(2)); + for (auto &op : methList->operands()) + MethodList.push_back(ObjCMethod(this, cast(&op))); + } + return MethodList; +} + +Expected BitcodeMetadata::getMethodName(ObjCMethod Data) const { + auto objcMeth = Data.getRawContent().getLLVMConstant(); + auto NameVar = dyn_cast( + GetUnderlyingObject(objcMeth->getOperand(0), getDataLayout(), 2)); + if (!NameVar) + return make_error("Could not find property name", + object_error::parse_failed); + + ConstantDataSequential *CD = + dyn_cast(NameVar->getInitializer()); + if (!CD) + return make_error("Could not parse property name", + object_error::parse_failed); + return CD->getAsString().rtrim('\0'); +} + +Expected BitcodeMetadata::getMethodType(ObjCMethod Data) const { + auto objcMeth = Data.getRawContent().getLLVMConstant(); + auto TypeVar = dyn_cast( + GetUnderlyingObject(objcMeth->getOperand(1), getDataLayout(), 2)); + if (!TypeVar) + return make_error("Could not find property name", + object_error::parse_failed); + + ConstantDataSequential *CD = + dyn_cast(TypeVar->getInitializer()); + if (!CD) + return make_error("Could not parse property name", + object_error::parse_failed); + return CD->getAsString().rtrim('\0'); +} + +Expected BitcodeMetadata::referencedSelectors() const { + ObjCSelectorList SelectorList; + for (auto &GV : OwningModule->globals()) { + if (GV.hasSection()) { + StringRef Section = GV.getSection(); + std::string SectionName; + // Trim all spaces in the section name. + for (auto c : Section) { + if (c != ' ') + SectionName.push_back(c); + } + if (SectionName == + "__DATA,__objc_selrefs,literal_pointers,no_dead_strip") { + SelectorList.push_back(ObjCSelectorRef(this, &GV)); + } + } + } + return SelectorList; +} + +Expected +BitcodeMetadata::getObjCSelectorName(ObjCSelectorRef Ref) const { + auto GV = dyn_cast(Ref.getRawContent().getLLVMConstant()); + auto sel_global = + GetUnderlyingObject(GV->getInitializer(), getDataLayout(), 2); + auto CD = dyn_cast(GlobalOpToUser(sel_global)); + if (!CD) + return make_error("could not read selref", + object_error::parse_failed); + return CD->getAsString().rtrim('\0'); +} + +void BitcodeMetadata::getAllPotentiallyDefinedSelectors( + StringSet<> &Set) const { + auto ignoreError = [](Error &&E) { + handleAllErrors(std::move(E), [&](const ErrorInfoBase &EI) { + return; // ignore. + }); + }; + if (auto selectors = referencedSelectors()) { + for (auto &sel : *selectors) { + auto GV = dyn_cast(sel.getRawContent().getLLVMConstant()); + auto sel_global = + GetUnderlyingObject(GV->getInitializer(), getDataLayout(), 2); + if (hasUserInMethodList(sel_global, OwningModule)) { + if (auto name = sel.getSelector()) + Set.insert(*name); + else + ignoreError(name.takeError()); + } + } + } else + ignoreError(selectors.takeError()); +} + +bool BitcodeMetadata::isObjC1() const { return !isObjC2(); } + +bool BitcodeMetadata::isObjC2() const { + auto versionMD = OwningModule->getModuleFlag("Objective-C Version"); + if (versionMD) { + auto objcVer = + cast(cast(versionMD)->getValue()) + ->getZExtValue(); + if (objcVer == 2) { + return true; + } + } + return false; +} + +Expected +BitcodeMetadata::getObjC2ClassRO(ObjCClass Data) const { + ConstantStruct *objc_class_t = nullptr; + if (auto objc_class = + dyn_cast(Data.getRawContent().getLLVMConstant())) { + objc_class_t = cast( + GlobalOpToUser(GetUnderlyingValue(objc_class, getDataLayout(), 2))); + } else + return make_error("Unexpected type for objc class", + object_error::parse_failed); + + int64_t class_offset = Data.getRawContent().getOffset(); + // read class_ro + unsigned offset = + is64Bit() ? offsetof(class64_t, data) : offsetof(class32_t, data); + auto class_ro_v = GetValueAtStructOffset(objc_class_t, class_offset + offset, + getDataLayout()); + if (auto CE = dyn_cast(class_ro_v)) { + // swift generates class_ro with an offset using add constant expr + class_ro_v = CE->getOperand(0); + } + auto class_ro_global = dyn_cast( + GetUnderlyingValue(class_ro_v, getDataLayout(), 2)); + if (!class_ro_global) + return make_error("Could not read ObjCClass RO data", + object_error::parse_failed); + auto class_ro = dyn_cast(GlobalOpToUser( + GetUnderlyingValue(class_ro_global, getDataLayout(), 4))); + if (!class_ro) + return make_error("Could not process ObjCClass RO data", + object_error::parse_failed); + return class_ro; +} + +StringRef BitcodeMetadata::getSymbolNameFromRef(ObjCRef Ref) const { + return Ref.getLLVMConstant()->getName(); +} + +StringRef BitcodeMetadata::guessClassNameBasedOnSymbol(StringRef Sym) const { + if (Sym.startswith("OBJC_CLASS_$_")) + return Sym.drop_front(strlen("OBJC_CLASS_$_")); + else if (Sym.startswith("\01.objc_class_name_")) + return Sym.drop_front(strlen("\01.objc_class_name_")); + else + return Sym; +} diff --git a/llvm/lib/ObjCMetadata/ObjCMachOBinary.cpp b/llvm/lib/ObjCMetadata/ObjCMachOBinary.cpp new file mode 100644 index 00000000000..a8613dd6854 --- /dev/null +++ b/llvm/lib/ObjCMetadata/ObjCMachOBinary.cpp @@ -0,0 +1,1356 @@ +//===- ObjCMachOBinary.cpp - Mach-O ObjC Metadata Reader --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the classes needed to parse ObjCMetadata in MachO Object. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ObjCMetadata/ObjCMetadata.h" +#include "llvm/ObjCMetadata/ObjCMachOBinary.h" +#include "llvm/Object/MachO.h" + +#include "macho-obj.h" + +using namespace llvm; +using namespace object; + +Expected MachOMetadata::classes() const { + if (isObjC1()) + return make_error("Unsupported ObjC Runtime", + object_error::parse_failed); + ObjCClassList ClassList; + SectionRef ClassSect = getSection("__OBJC2", "__class_list"); + if (ClassSect == SectionRef()) + ClassSect = getSection("__DATA", "__objc_classlist"); + // Return empty if the section doesn't exists. + if (ClassSect == SectionRef()) + return ClassList; + if (ClassSect.getSize() % OwningBinary->getBytesInAddress() != 0) + return make_error( + "objc class section size is not a multiple of pointer size", + object_error::parse_failed); + + for (unsigned i = 0; i < ClassSect.getSize(); + i += OwningBinary->getBytesInAddress()) + ClassList.push_back(ObjCClassRef(this, ClassSect.getAddress() + i)); + + return ClassList; +} + +Expected MachOMetadata::getObjCClassFromRef(ObjCClassRef Ref) const { + if (OwningBinary->is64Bit()) { + if (auto class_ptr = + getResolvedValueFromAddress64(Ref.getRawContent().getValue())) + return ObjCClass(this, *class_ptr); + else + return class_ptr.takeError(); + } else { + if (auto class_ptr = + getResolvedValueFromAddress32(Ref.getRawContent().getValue())) + return ObjCClass(this, *class_ptr); + else + return class_ptr.takeError(); + } +} + +bool MachOMetadata::isObjCClassExternal(ObjCClassRef Ref) const { + if (OwningBinary->is64Bit()) { + if (auto addr = + getResolvedValueFromAddress64(Ref.getRawContent().getValue())) { + if (*addr == 0) + return true; + else + return false; + } else { + // assume external if can't get class. + // ignore error and return true. + handleAllErrors(addr.takeError(), + [&](const ErrorInfoBase &EI) { return; }); + return true; + } + } else { + if (auto addr = + getResolvedValueFromAddress32(Ref.getRawContent().getValue())) { + if (*addr == 0) + return true; + else + return false; + } else { + // assume external if can't get class. + // ignore error and return true. + handleAllErrors(addr.takeError(), + [&](const ErrorInfoBase &EI) { return; }); + return true; + } + } +} + +StringRef MachOMetadata::getSymbolNameFromRef(ObjCRef Ref) const { + uint64_t offset; + if (auto symName = + getSymbolFromAddress(Ref.getValue(), offset)) + return *symName; + else { + // ignore error and return empty string. + handleAllErrors(symName.takeError(), + [&](const ErrorInfoBase &EI) { return; }); + return StringRef(); + } +} + +StringRef MachOMetadata::guessClassNameBasedOnSymbol(StringRef Sym) const { + if (Sym.startswith("_OBJC_CLASS_$_")) + return Sym.drop_front(strlen("_OBJC_CLASS_$_")); + else if (Sym.startswith(".objc_class_name_")) + return Sym.drop_front(strlen(".objc_class_name_")); + else + return Sym; +} + +Expected MachOMetadata::categories() const { + if (isObjC1()) + return make_error("Unsupported ObjC Runtime", + object_error::parse_failed); + ObjCCategoryList CategoryList; + SectionRef CatSect = getSection("__OBJC2", "__category_list"); + if (CatSect == SectionRef()) + CatSect = getSection("__DATA", "__objc_catlist"); + // Return empty if the section doesn't exists. + if (CatSect == SectionRef()) + return CategoryList; + if (CatSect.getSize() % OwningBinary->getBytesInAddress() != 0) + return make_error( + "objc category section size is not a multiple of pointer size", + object_error::parse_failed); + + for (unsigned i = 0; i < CatSect.getSize(); + i += OwningBinary->getBytesInAddress()) + CategoryList.push_back(ObjCCategoryRef(this, CatSect.getAddress() + i)); + + return CategoryList; +} + +Expected +MachOMetadata::getObjCCategoryFromRef(ObjCCategoryRef Ref) const { + if (OwningBinary->is64Bit()) { + if (auto cat_ptr = + getResolvedValueFromAddress64(Ref.getRawContent().getValue())) + return ObjCCategory(this, *cat_ptr); + else + return cat_ptr.takeError(); + } else { + if (auto cat_ptr = + getResolvedValueFromAddress32(Ref.getRawContent().getValue())) + return ObjCCategory(this, *cat_ptr); + else + return cat_ptr.takeError(); + } +} + +Expected MachOMetadata::protocols() const { + if (isObjC1()) + return make_error("Unsupported ObjC Runtime", + object_error::parse_failed); + ObjCProtocolList ProtocolList; + SectionRef ProtoSect = getSection("__OBJC2", "__protocol_list"); + if (ProtoSect == SectionRef()) + ProtoSect = getSection("__DATA", "__objc_protolist"); + // Return empty if the section doesn't exists. + if (ProtoSect == SectionRef()) + return ProtocolList; + if (ProtoSect.getSize() % OwningBinary->getBytesInAddress() != 0) + return make_error( + "objc protocol section size is not a multiple of pointer size", + object_error::parse_failed); + + for (unsigned i = 0; i < ProtoSect.getSize(); + i += OwningBinary->getBytesInAddress()) + ProtocolList.push_back(ObjCProtocolRef(this, ProtoSect.getAddress() + i)); + + return ProtocolList; +} + +Expected +MachOMetadata::getObjCProtocolFromRef(ObjCProtocolRef Ref) const { + if (OwningBinary->is64Bit()) { + if (auto proto_ptr = + getResolvedValueFromAddress64(Ref.getRawContent().getValue())) + return ObjCProtocol(this, *proto_ptr); + else + return proto_ptr.takeError(); + } else { + if (auto proto_ptr = + getResolvedValueFromAddress32(Ref.getRawContent().getValue())) + return ObjCProtocol(this, *proto_ptr); + else + return proto_ptr.takeError(); + } +} + +Expected MachOMetadata::getObjCClassName(ObjCClass ClassRef) const { + if (isObjC1()) + return make_error("Unsupported ObjC Runtime", + object_error::parse_failed); + if (OwningBinary->is64Bit()) { + if (auto ro = getObjC2ClassRO64(ClassRef)) { + if (auto name = getResolvedValueFromAddress64( + ro->getValue() + offsetof(class_ro64_t, name))) + return getStringFromAddress(*name); + else + return name.takeError(); + } else + return ro.takeError(); + } else { + if (auto ro = getObjC2ClassRO32(ClassRef)) { + if (auto name = getResolvedValueFromAddress32( + ro->getValue() + offsetof(class_ro32_t, name))) + return getStringFromAddress(*name); + else + return name.takeError(); + } else + return ro.takeError(); + } +} + +Expected MachOMetadata::getObjCSuperClassName(ObjCClass Data) const { + if (isObjC1()) + return make_error("Unsupported ObjC Runtime", + object_error::parse_failed); + if (OwningBinary->is64Bit()) { + uint64_t superAddr = + Data.getRawContent().getValue() + offsetof(class64_t, superclass); + ObjCClassRef super(this, superAddr); + if (auto superName = super.getClassName()) + return *superName; + else + return superName.takeError(); + } else { + uint64_t superAddr = + Data.getRawContent().getValue() + offsetof(class32_t, superclass); + ObjCClassRef super(this, superAddr); + if (auto superName = super.getClassName()) + return *superName; + else + return superName.takeError(); + } +} + +Expected MachOMetadata::isObjCClassSwift(ObjCClass Data) const { + if (isObjC1()) + return false; + if (OwningBinary->is64Bit()) { + if (auto class_ro = getResolvedValueFromAddress64( + Data.getRawContent().getValue() + offsetof(class64_t, data))) { + return ((*class_ro & ~0x7) != 0); + } else + return class_ro.takeError(); + } else { + if (auto class_ro = getResolvedValueFromAddress32( + Data.getRawContent().getValue() + offsetof(class32_t, data))) { + return ((*class_ro & ~0x3) != 0); + } else + return class_ro.takeError(); + } +} + +Expected MachOMetadata::isObjCClassMetaClass(ObjCClass Data) const { + if (isObjC1()) + return false; + if (OwningBinary->is64Bit()) { + if (auto class_ro = getObjC2ClassRO64(Data)) { + uint32_t f; + if (auto flags = getValueFromAddress(class_ro->getValue() + + offsetof(class_ro64_t, flags), + sizeof(f))) { + memcpy(&f, flags->data(), sizeof(f)); + if (OwningBinary->isLittleEndian() != sys::IsLittleEndianHost) + sys::swapByteOrder(f); + return ((f & RO_META) != 0); + } else + return flags.takeError(); + } else + return class_ro.takeError(); + } else { + if (auto class_ro = getObjC2ClassRO32(Data)) { + uint32_t f; + if (auto flags = getValueFromAddress(class_ro->getValue() + + offsetof(class_ro32_t, flags), + sizeof(f))) { + memcpy(&f, flags->data(), sizeof(f)); + if (OwningBinary->isLittleEndian() != sys::IsLittleEndianHost) + sys::swapByteOrder(f); + return ((f & RO_META) != 0); + } else + return flags.takeError(); + } else + return class_ro.takeError(); + } +} + +Expected +MachOMetadata::materializePropertyList(ObjCClass ClassRef) const { + if (isObjC1()) + return make_error("Unsupported ObjC Runtime", + object_error::parse_failed); + ObjCPropertyList PropertyList; + if (OwningBinary->is64Bit()) { + if (auto ro = getObjC2ClassRO64(ClassRef)) { + if (auto baseProperties = getResolvedValueFromAddress64( + ro->getValue() + offsetof(class_ro64_t, baseProperties))) { + if (!*baseProperties) + return PropertyList; + objc_property_list64 propList; + if (auto pList = + getValueFromAddress(*baseProperties, sizeof(propList))) + memcpy(&propList, pList->data(), sizeof(propList)); + else + return pList.takeError(); + if (OwningBinary->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(propList); + for (unsigned i = 0; i < propList.count; ++ i) + PropertyList.push_back(ObjCProperty( + this, *baseProperties + sizeof(objc_property_list64) + + i * sizeof(objc_property64))); + return PropertyList; + } else + return baseProperties.takeError(); + } else + return ro.takeError(); + } else { + if (auto ro = getObjC2ClassRO32(ClassRef)) { + if (auto baseProperties = getResolvedValueFromAddress32( + ro->getValue() + offsetof(class_ro32_t, baseProperties))) { + if (!*baseProperties) + return PropertyList; + objc_property_list32 propList; + if (auto pList = + getValueFromAddress(*baseProperties, sizeof(propList))) + memcpy(&propList, pList->data(), sizeof(propList)); + else + return pList.takeError(); + if (OwningBinary->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(propList); + for (unsigned i = 0; i < propList.count; ++ i) + PropertyList.push_back(ObjCProperty( + this, *baseProperties + sizeof(objc_property_list32) + + i * sizeof(objc_property32))); + return PropertyList; + } else + return baseProperties.takeError(); + } else + return ro.takeError(); + } +} + +Expected MachOMetadata::getPropertyName(ObjCProperty Data) const { + if (isObjC1()) + return make_error("Unsupported ObjC Runtime", + object_error::parse_failed); + if (OwningBinary->is64Bit()) { + if (auto propName = getResolvedValueFromAddress64( + Data.getRawContent().getValue() + offsetof(objc_property64, name))) + return getStringFromAddress(*propName); + else + return propName.takeError(); + } else { + if (auto propName = getResolvedValueFromAddress32( + Data.getRawContent().getValue() + offsetof(objc_property32, name))) + return getStringFromAddress(*propName); + else + return propName.takeError(); + } +} + +Expected +MachOMetadata::getPropertyAttribute(ObjCProperty Data) const { + if (isObjC1()) + return make_error("Unsupported ObjC Runtime", + object_error::parse_failed); + if (OwningBinary->is64Bit()) { + if (auto propAttr = getResolvedValueFromAddress64( + Data.getRawContent().getValue() + + offsetof(objc_property64, attributes))) + return getStringFromAddress(*propAttr); + else + return propAttr.takeError(); + } else { + if (auto propAttr = getResolvedValueFromAddress32( + Data.getRawContent().getValue() + + offsetof(objc_property32, attributes))) + return getStringFromAddress(*propAttr); + else + return propAttr.takeError(); + } +} + +Expected +MachOMetadata::materializeInstanceMethodList(ObjCClass ClassRef) const { + if (isObjC1()) + return make_error("Unsupported ObjC Runtime", + object_error::parse_failed); + ObjCMethodList MethodList; + if (OwningBinary->is64Bit()) { + if (auto ro = getObjC2ClassRO64(ClassRef)) { + if (auto baseMethods = getResolvedValueFromAddress64( + ro->getValue() + offsetof(class_ro64_t, baseMethods))) { + if (!*baseMethods) + return MethodList; + method_list64_t methodList; + if (auto mList = + getValueFromAddress(*baseMethods, sizeof(methodList))) + memcpy(&methodList, mList->data(), sizeof(methodList)); + else + return mList.takeError(); + if (OwningBinary->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(methodList); + for (unsigned i = 0; i < methodList.count; ++i) + MethodList.push_back(ObjCMethod(this, *baseMethods + + sizeof(method_list64_t) + + i * sizeof(method64_t))); + return MethodList; + } else + return baseMethods.takeError(); + } else + return ro.takeError(); + } else { + if (auto ro = getObjC2ClassRO32(ClassRef)) { + if (auto baseMethods = getResolvedValueFromAddress32( + ro->getValue() + offsetof(class_ro32_t, baseMethods))) { + if (!*baseMethods) + return MethodList; + method_list32_t methodList; + if (auto mList = + getValueFromAddress(*baseMethods, sizeof(methodList))) + memcpy(&methodList, mList->data(), sizeof(methodList)); + else + return mList.takeError(); + if (OwningBinary->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(methodList); + for (unsigned i = 0; i < methodList.count; ++i) + MethodList.push_back(ObjCMethod(this, *baseMethods + + sizeof(method_list32_t) + + i * sizeof(method32_t))); + return MethodList; + } else + return baseMethods.takeError(); + } else + return ro.takeError(); + } +} + +Expected +MachOMetadata::materializeClassMethodList(ObjCClass ClassRef) const { + if (isObjC1()) + return make_error("Unsupported ObjC Runtime", + object_error::parse_failed); + if (OwningBinary->is64Bit()) { + if (auto meta = getObjC2MetaClass64(ClassRef)) { + if (meta->getRawContent().getValue() == 0) + return ObjCMethodList(); + else + return materializeInstanceMethodList(*meta); + } else + return meta.takeError(); + } else { + if (auto meta = getObjC2MetaClass32(ClassRef)) { + if (meta->getRawContent().getValue() == 0) + return ObjCMethodList(); + else + return materializeInstanceMethodList(*meta); + } else + return meta.takeError(); + } +} + +Expected MachOMetadata::getObjCCategoryName(ObjCCategory Cat) const { + if (isObjC1()) + return make_error("Unsupported ObjC Runtime", + object_error::parse_failed); + if (OwningBinary->is64Bit()) { + if (auto name = getResolvedValueFromAddress64( + Cat.getRawContent().getValue() + offsetof(category64_t, name))) + return getStringFromAddress(*name); + else + return name.takeError(); + } else { + if (auto name = getResolvedValueFromAddress32( + Cat.getRawContent().getValue() + offsetof(category32_t, name))) + return getStringFromAddress(*name); + else + return name.takeError(); + } +} + +Expected +MachOMetadata::getObjCCategoryBaseClassName(ObjCCategory Cat) const { + if (isObjC1()) + return make_error("Unsupported ObjC Runtime", + object_error::parse_failed); + if (OwningBinary->is64Bit()) { + uint64_t baseClassAddr = + Cat.getRawContent().getValue() + offsetof(category64_t, cls); + ObjCClassRef cls(this, baseClassAddr); + if (auto clsName = cls.getClassName()) + return *clsName; + else + return clsName.takeError(); + } else { + uint64_t baseClassAddr = + Cat.getRawContent().getValue() + offsetof(category32_t, cls); + ObjCClassRef cls(this, baseClassAddr); + if (auto clsName = cls.getClassName()) + return *clsName; + else + return clsName.takeError(); + } +} + +Expected +MachOMetadata::materializePropertyList(ObjCCategory Data) const { + if (isObjC1()) + return make_error("Unsupported ObjC Runtime", + object_error::parse_failed); + ObjCPropertyList PropertyList; + if (OwningBinary->is64Bit()) { + if (auto iprop = getResolvedValueFromAddress64( + Data.getRawContent().getValue() + + offsetof(category64_t, instanceProperties))) { + if (*iprop == 0) + return PropertyList; + + objc_property_list64 propList; + if (auto pList = getValueFromAddress(*iprop, sizeof(propList))) + memcpy(&propList, pList->data(), sizeof(propList)); + else + return pList.takeError(); + if (OwningBinary->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(propList); + for (unsigned i = 0; i < propList.count; ++ i) + PropertyList.push_back( + ObjCProperty(this, *iprop + sizeof(objc_property_list64) + + i * sizeof(objc_property64))); + return PropertyList; + } else + return iprop.takeError(); + } else { + if (auto iprop = getResolvedValueFromAddress32( + Data.getRawContent().getValue() + + offsetof(category32_t, instanceProperties))) { + if (*iprop == 0) + return PropertyList; + + objc_property_list32 propList; + if (auto pList = getValueFromAddress(*iprop, sizeof(propList))) + memcpy(&propList, pList->data(), sizeof(propList)); + else + return pList.takeError(); + if (OwningBinary->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(propList); + for (unsigned i = 0; i < propList.count; ++ i) + PropertyList.push_back( + ObjCProperty(this, *iprop + sizeof(objc_property_list32) + + i * sizeof(objc_property32))); + return PropertyList; + } else + return iprop.takeError(); + } +} + +Expected +MachOMetadata::materializeInstanceMethodList(ObjCCategory Data) const { + if (isObjC1()) + return make_error("Unsupported ObjC Runtime", + object_error::parse_failed); + ObjCMethodList MethodList; + if (OwningBinary->is64Bit()) { + if (auto iprop = getResolvedValueFromAddress64( + Data.getRawContent().getValue() + + offsetof(category64_t, instanceMethods))) { + if (*iprop == 0) + return MethodList; + + method_list64_t methodList; + if (auto mList = getValueFromAddress(*iprop, sizeof(methodList))) + memcpy(&methodList, mList->data(), sizeof(methodList)); + else + return mList.takeError(); + if (OwningBinary->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(methodList); + for (unsigned i = 0; i < methodList.count; ++ i) + MethodList.push_back(ObjCMethod(this, *iprop + sizeof(method_list64_t) + + i * sizeof(method64_t))); + return MethodList; + } else + return iprop.takeError(); + } else { + if (auto iprop = getResolvedValueFromAddress32( + Data.getRawContent().getValue() + + offsetof(category32_t, instanceMethods))) { + if (*iprop == 0) + return MethodList; + + method_list32_t methodList; + if (auto mList = getValueFromAddress(*iprop, sizeof(methodList))) + memcpy(&methodList, mList->data(), sizeof(methodList)); + else + return mList.takeError(); + if (OwningBinary->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(methodList); + for (unsigned i = 0; i < methodList.count; ++ i) + MethodList.push_back(ObjCMethod(this, *iprop + sizeof(method_list32_t) + + i * sizeof(method32_t))); + return MethodList; + } else + return iprop.takeError(); + } +} + +Expected +MachOMetadata::materializeClassMethodList(ObjCCategory Data) const { + if (isObjC1()) + return make_error("Unsupported ObjC Runtime", + object_error::parse_failed); + ObjCMethodList MethodList; + if (OwningBinary->is64Bit()) { + if (auto iprop = getResolvedValueFromAddress64( + Data.getRawContent().getValue() + + offsetof(category64_t, classMethods))) { + if (*iprop == 0) + return MethodList; + + method_list64_t methodList; + if (auto mList = getValueFromAddress(*iprop, sizeof(methodList))) + memcpy(&methodList, mList->data(), sizeof(methodList)); + else + return mList.takeError(); + if (OwningBinary->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(methodList); + for (unsigned i = 0; i < methodList.count; ++ i) + MethodList.push_back(ObjCMethod(this, *iprop + sizeof(method_list64_t) + + i * sizeof(method64_t))); + return MethodList; + } else + return iprop.takeError(); + } else { + if (auto iprop = getResolvedValueFromAddress32( + Data.getRawContent().getValue() + + offsetof(category32_t, classMethods))) { + if (*iprop == 0) + return MethodList; + + method_list32_t methodList; + if (auto mList = getValueFromAddress(*iprop, sizeof(methodList))) + memcpy(&methodList, mList->data(), sizeof(methodList)); + else + return mList.takeError(); + if (OwningBinary->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(methodList); + for (unsigned i = 0; i < methodList.count; ++ i) + MethodList.push_back(ObjCMethod(this, *iprop + sizeof(method_list32_t) + + i * sizeof(method32_t))); + return MethodList; + } else + return iprop.takeError(); + } +} + +Expected +MachOMetadata::getObjCProtocolName(ObjCProtocol Data) const { + if (isObjC1()) + return make_error("Unsupported ObjC Runtime", + object_error::parse_failed); + if (OwningBinary->is64Bit()) { + if (auto name = getResolvedValueFromAddress64( + Data.getRawContent().getValue() + offsetof(protocol64_t, name))) + return getStringFromAddress(*name); + else + return name.takeError(); + } else { + if (auto name = getResolvedValueFromAddress32( + Data.getRawContent().getValue() + offsetof(protocol32_t, name))) + return getStringFromAddress(*name); + else + return name.takeError(); + } +} + +Expected +MachOMetadata::materializePropertyList(ObjCProtocol Data) const { + if (isObjC1()) + return make_error("Unsupported ObjC Runtime", + object_error::parse_failed); + ObjCPropertyList PropertyList; + if (OwningBinary->is64Bit()) { + if (auto iprop = getResolvedValueFromAddress64( + Data.getRawContent().getValue() + + offsetof(protocol64_t, instanceProperties))) { + if (*iprop == 0) + return PropertyList; + + objc_property_list64 propList; + if (auto pList = getValueFromAddress(*iprop, sizeof(propList))) + memcpy(&propList, pList->data(), sizeof(propList)); + else + return pList.takeError(); + if (OwningBinary->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(propList); + for (unsigned i = 0; i < propList.count; ++ i) + PropertyList.push_back( + ObjCProperty(this, *iprop + sizeof(objc_property_list64) + + i * sizeof(objc_property64))); + return PropertyList; + } else + return iprop.takeError(); + } else { + if (auto iprop = getResolvedValueFromAddress32( + Data.getRawContent().getValue() + + offsetof(protocol32_t, instanceProperties))) { + if (*iprop == 0) + return PropertyList; + + objc_property_list32 propList; + if (auto pList = getValueFromAddress(*iprop, sizeof(propList))) + memcpy(&propList, pList->data(), sizeof(propList)); + else + return pList.takeError(); + if (OwningBinary->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(propList); + for (unsigned i = 0; i < propList.count; ++ i) + PropertyList.push_back( + ObjCProperty(this, *iprop + sizeof(objc_property_list32) + + i * sizeof(objc_property32))); + return PropertyList; + } else + return iprop.takeError(); + } +} + +Expected +MachOMetadata::materializeInstanceMethodList(ObjCProtocol Data) const { + if (isObjC1()) + return make_error("Unsupported ObjC Runtime", + object_error::parse_failed); + ObjCMethodList MethodList; + if (OwningBinary->is64Bit()) { + if (auto iprop = getResolvedValueFromAddress64( + Data.getRawContent().getValue() + + offsetof(protocol64_t, instanceMethods))) { + if (*iprop == 0) + return MethodList; + + method_list64_t methodList; + if (auto mList = getValueFromAddress(*iprop, sizeof(methodList))) + memcpy(&methodList, mList->data(), sizeof(methodList)); + else + return mList.takeError(); + if (OwningBinary->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(methodList); + for (unsigned i = 0; i < methodList.count; ++ i) + MethodList.push_back(ObjCMethod(this, *iprop + sizeof(method_list64_t) + + i * sizeof(method64_t))); + return MethodList; + } else + return iprop.takeError(); + } else { + if (auto iprop = getResolvedValueFromAddress32( + Data.getRawContent().getValue() + + offsetof(protocol32_t, instanceMethods))) { + if (*iprop == 0) + return MethodList; + + method_list32_t methodList; + if (auto mList = getValueFromAddress(*iprop, sizeof(methodList))) + memcpy(&methodList, mList->data(), sizeof(methodList)); + else + return mList.takeError(); + if (OwningBinary->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(methodList); + for (unsigned i = 0; i < methodList.count; ++ i) + MethodList.push_back(ObjCMethod(this, *iprop + sizeof(method_list32_t) + + i * sizeof(method32_t))); + return MethodList; + } else + return iprop.takeError(); + } +} + +Expected +MachOMetadata::materializeClassMethodList(ObjCProtocol Data) const { + if (isObjC1()) + return make_error("Unsupported ObjC Runtime", + object_error::parse_failed); + ObjCMethodList MethodList; + if (OwningBinary->is64Bit()) { + if (auto iprop = getResolvedValueFromAddress64( + Data.getRawContent().getValue() + + offsetof(protocol64_t, classMethods))) { + if (*iprop == 0) + return MethodList; + + method_list64_t methodList; + if (auto mList = getValueFromAddress(*iprop, sizeof(methodList))) + memcpy(&methodList, mList->data(), sizeof(methodList)); + else + return mList.takeError(); + if (OwningBinary->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(methodList); + for (unsigned i = 0; i < methodList.count; ++ i) + MethodList.push_back(ObjCMethod(this, *iprop + sizeof(method_list64_t) + + i * sizeof(method64_t))); + return MethodList; + } else + return iprop.takeError(); + } else { + if (auto iprop = getResolvedValueFromAddress32( + Data.getRawContent().getValue() + + offsetof(protocol32_t, classMethods))) { + if (*iprop == 0) + return MethodList; + + method_list32_t methodList; + if (auto mList = getValueFromAddress(*iprop, sizeof(methodList))) + memcpy(&methodList, mList->data(), sizeof(methodList)); + else + return mList.takeError(); + if (OwningBinary->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(methodList); + for (unsigned i = 0; i < methodList.count; ++ i) + MethodList.push_back(ObjCMethod(this, *iprop + sizeof(method_list32_t) + + i * sizeof(method32_t))); + return MethodList; + } else + return iprop.takeError(); + } +} + +Expected +MachOMetadata::materializeOptionalInstanceMethodList(ObjCProtocol Data) const { + if (isObjC1()) + return make_error("Unsupported ObjC Runtime", + object_error::parse_failed); + ObjCMethodList MethodList; + if (OwningBinary->is64Bit()) { + if (auto iprop = getResolvedValueFromAddress64( + Data.getRawContent().getValue() + + offsetof(protocol64_t, optionalInstanceMethods))) { + if (*iprop == 0) + return MethodList; + + method_list64_t methodList; + if (auto mList = getValueFromAddress(*iprop, sizeof(methodList))) + memcpy(&methodList, mList->data(), sizeof(methodList)); + else + return mList.takeError(); + if (OwningBinary->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(methodList); + for (unsigned i = 0; i < methodList.count; ++ i) + MethodList.push_back(ObjCMethod(this, *iprop + sizeof(method_list64_t) + + i * sizeof(method64_t))); + return MethodList; + } else + return iprop.takeError(); + } else { + if (auto iprop = getResolvedValueFromAddress32( + Data.getRawContent().getValue() + + offsetof(protocol32_t, optionalInstanceMethods))) { + if (*iprop == 0) + return MethodList; + + method_list32_t methodList; + if (auto mList = getValueFromAddress(*iprop, sizeof(methodList))) + memcpy(&methodList, mList->data(), sizeof(methodList)); + else + return mList.takeError(); + if (OwningBinary->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(methodList); + for (unsigned i = 0; i < methodList.count; ++ i) + MethodList.push_back(ObjCMethod(this, *iprop + sizeof(method_list32_t) + + i * sizeof(method32_t))); + return MethodList; + } else + return iprop.takeError(); + } +} + +Expected +MachOMetadata::materializeOptionalClassMethodList(ObjCProtocol Data) const { + if (isObjC1()) + return make_error("Unsupported ObjC Runtime", + object_error::parse_failed); + ObjCMethodList MethodList; + if (OwningBinary->is64Bit()) { + if (auto iprop = getResolvedValueFromAddress64( + Data.getRawContent().getValue() + + offsetof(protocol64_t, optionalClassMethods))) { + if (*iprop == 0) + return MethodList; + + method_list64_t methodList; + if (auto mList = getValueFromAddress(*iprop, sizeof(methodList))) + memcpy(&methodList, mList->data(), sizeof(methodList)); + else + return mList.takeError(); + if (OwningBinary->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(methodList); + for (unsigned i = 0; i < methodList.count; ++ i) + MethodList.push_back(ObjCMethod(this, *iprop + sizeof(method_list64_t) + + i * sizeof(method64_t))); + return MethodList; + } else + return iprop.takeError(); + } else { + if (auto iprop = getResolvedValueFromAddress32( + Data.getRawContent().getValue() + + offsetof(protocol32_t, optionalClassMethods))) { + if (*iprop == 0) + return MethodList; + + method_list32_t methodList; + if (auto mList = getValueFromAddress(*iprop, sizeof(methodList))) + memcpy(&methodList, mList->data(), sizeof(methodList)); + else + return mList.takeError(); + if (OwningBinary->isLittleEndian() != sys::IsLittleEndianHost) + swapStruct(methodList); + for (unsigned i = 0; i < methodList.count; ++ i) + MethodList.push_back(ObjCMethod(this, *iprop + sizeof(method_list32_t) + + i * sizeof(method32_t))); + return MethodList; + } else + return iprop.takeError(); + } +} + +Expected MachOMetadata::getMethodName(ObjCMethod Data) const { + if (isObjC1()) + return make_error("Unsupported ObjC Runtime", + object_error::parse_failed); + if (OwningBinary->is64Bit()) { + if (auto methodName = getResolvedValueFromAddress64( + Data.getRawContent().getValue() + offsetof(method64_t, name))) + return getStringFromAddress(*methodName); + else + return methodName.takeError(); + } else { + if (auto methodName = getResolvedValueFromAddress32( + Data.getRawContent().getValue() + offsetof(method32_t, name))) + return getStringFromAddress(*methodName); + else + return methodName.takeError(); + } +} + +Expected MachOMetadata::getMethodType(ObjCMethod Data) const { + if (isObjC1()) + return make_error("Unsupported ObjC Runtime", + object_error::parse_failed); + if (OwningBinary->is64Bit()) { + if (auto methodTypes = getResolvedValueFromAddress64( + Data.getRawContent().getValue() + offsetof(method64_t, types))) + return getStringFromAddress(*methodTypes); + else + return methodTypes.takeError(); + } else { + if (auto methodTypes = getResolvedValueFromAddress32( + Data.getRawContent().getValue() + offsetof(method32_t, types))) + return getStringFromAddress(*methodTypes); + else + return methodTypes.takeError(); + } +} + +Expected MachOMetadata::referencedSelectors() const { + if (isObjC1()) + return make_error("Unsupported ObjC Runtime", + object_error::parse_failed); + ObjCSelectorList SelectorList; + SectionRef SelSect = getSection("__DATA", "__objc_selrefs"); + // Return empty if the section doesn't exists. + if (SelSect == SectionRef()) + return SelectorList; + + if (SelSect.getSize() % OwningBinary->getBytesInAddress() != 0) + return make_error( + "objc class section size is not a multiple of pointer size", + object_error::parse_failed); + + for (unsigned i = 0; i < SelSect.getSize(); + i += OwningBinary->getBytesInAddress()) + SelectorList.push_back(ObjCSelectorRef(this, SelSect.getAddress() + i)); + + return SelectorList; +} + +Expected +MachOMetadata::getObjCSelectorName(ObjCSelectorRef Ref) const { + if (isObjC1()) + return make_error("Unsupported ObjC Runtime", + object_error::parse_failed); + if (OwningBinary->is64Bit()) { + if (auto sel = + getResolvedValueFromAddress64(Ref.getRawContent().getValue())) + return getStringFromAddress(*sel); + else + return sel.takeError(); + } else { + if (auto sel = + getResolvedValueFromAddress32(Ref.getRawContent().getValue())) + return getStringFromAddress(*sel); + else + return sel.takeError(); + } +} + +void MachOMetadata::getAllPotentiallyDefinedSelectors(StringSet<> &Set) const { + // Clear the set. + Set.clear(); + SectionRef ObjCConstSect = getSection("__DATA", "__objc_const"); + SectionRef MethodNameSect = getSection("__TEXT", "__objc_methname"); + if (ObjCConstSect == SectionRef() || MethodNameSect == SectionRef()) + return; + + auto ignoreError = [](Error &&E) { + handleAllErrors(std::move(E), [&](const ErrorInfoBase &EI) { + return; // ignore. + }); + }; + + if (auto sels = referencedSelectors()) { + StringSet<> refSels; + for (auto sel : *sels) { + if (auto selName = sel.getSelector()) + refSels.insert(*selName); + else + ignoreError(selName.takeError()); + } + if (OwningBinary->is64Bit()) { + for (uint64_t A = ObjCConstSect.getAddress(), + E = ObjCConstSect.getAddress() + ObjCConstSect.getSize(); + A < E; A += OwningBinary->getBytesInAddress()) { + auto methodName = getResolvedValueFromAddress64(A); + if (!methodName) { + ignoreError(methodName.takeError()); + continue; + } + if (*methodName >= MethodNameSect.getAddress() && + *methodName < + MethodNameSect.getAddress() + MethodNameSect.getSize()) { + if (auto name = getStringFromAddress(*methodName)) { + if (refSels.find(*name) != refSels.end()) + Set.insert(*name); + } else + ignoreError(name.takeError()); + } + } + } else { + for (uint64_t A = ObjCConstSect.getAddress(), + E = ObjCConstSect.getAddress() + ObjCConstSect.getSize(); + A < E; A += OwningBinary->getBytesInAddress()) { + auto methodName = getResolvedValueFromAddress32(A); + if (!methodName) { + ignoreError(methodName.takeError()); + continue; + } + if (*methodName >= MethodNameSect.getAddress() && + *methodName < + MethodNameSect.getAddress() + MethodNameSect.getSize()) { + if (auto name = getStringFromAddress(*methodName)) { + if (refSels.find(*name) != refSels.end()) + Set.insert(*name); + } else + ignoreError(name.takeError()); + } + } + } + } else + ignoreError(sels.takeError()); +} + +bool MachOMetadata::isObjC1() const { return !isObjC2(); } + +bool MachOMetadata::isObjC2() const { + if (OwningBinary->is64Bit() || + OwningBinary->getHeader().cputype == MachO::CPU_TYPE_ARM) + return true; + + return !(getSection("__OBJC", "__module_info") == SectionRef()); +} + +const SectionRef MachOMetadata::getSection(const char *segname, + const char *sectname) const { + for (const SectionRef &Section : OwningBinary->sections()) { + StringRef SectName; + Section.getName(SectName); + DataRefImpl Ref = Section.getRawDataRefImpl(); + StringRef SegName = OwningBinary->getSectionFinalSegmentName(Ref); + if (SegName == segname && SectName == sectname) + return Section; + } + return SectionRef(); +} + +Expected MachOMetadata::getValueFromAddress(uint64_t Address, + uint64_t size, + bool objc_only) const { + if (isAddressEncrypted(Address)) + return make_error("Value at the address is encrypted", + object_error::parse_failed); + for (const SectionRef &Section : OwningBinary->sections()) { + uint64_t SectAddress = Section.getAddress(); + uint64_t SectSize = Section.getSize(); + if (SectSize == 0) + continue; + if (objc_only) { + StringRef SectName; + Section.getName(SectName); + DataRefImpl Ref = Section.getRawDataRefImpl(); + StringRef SegName = OwningBinary->getSectionFinalSegmentName(Ref); + if (SegName != "__OBJC" && SectName != "__cstring") + continue; + } + if (Address >= SectAddress && Address < SectAddress + SectSize) { + uint64_t offset = Address - SectAddress; + StringRef SectContents; + Section.getContents(SectContents); + // size == 0 implies the length of the string. + StringRef Target = (size == 0) + ? StringRef(SectContents.data() + offset) + : StringRef(SectContents.data() + offset, size); + if (Target.size() + offset > SectSize) + return make_error( + "Data extends pass the end of the section", + object_error::parse_failed); + return Target; + } + } + return make_error("Pointer out of bound", + object_error::parse_failed); +} + +bool MachOMetadata::isAddressEncrypted(uint64_t Address) const { + // Find encrypted range. + if (OwningBinary->is64Bit()) { + uint64_t fileOffset = 0; + for (const auto &LC : OwningBinary->load_commands()) { + if (LC.C.cmd == MachO::LC_SEGMENT_64) { + MachO::segment_command_64 cmd = + OwningBinary->getSegment64LoadCommand(LC); + if (Address > cmd.vmaddr && Address < cmd.vmaddr + cmd.vmsize) + fileOffset = Address - cmd.vmaddr + cmd.fileoff; + } + } + for (const auto &LC : OwningBinary->load_commands()) { + if (LC.C.cmd == MachO::LC_ENCRYPTION_INFO_64) { + MachO::encryption_info_command_64 cmd = + OwningBinary->getEncryptionInfoCommand64(LC); + if (cmd.cryptid == 0) + continue; + if (fileOffset > cmd.cryptoff && + fileOffset < cmd.cryptoff + cmd.cryptsize) + return true; + } + } + } else { + uint64_t fileOffset = 0; + for (const auto &LC : OwningBinary->load_commands()) { + if (LC.C.cmd == MachO::LC_SEGMENT) { + MachO::segment_command cmd = OwningBinary->getSegmentLoadCommand(LC); + if (Address > cmd.vmaddr && Address < cmd.vmaddr + cmd.vmsize) + fileOffset = Address - cmd.vmaddr + cmd.fileoff; + } + } + for (const auto &LC : OwningBinary->load_commands()) { + if (LC.C.cmd == MachO::LC_ENCRYPTION_INFO) { + MachO::encryption_info_command cmd = + OwningBinary->getEncryptionInfoCommand(LC); + if (cmd.cryptid == 0) + continue; + if (fileOffset > cmd.cryptoff && + fileOffset < cmd.cryptoff + cmd.cryptsize) + return true; + } + } + } + return false; +} + +Expected MachOMetadata::getStringFromAddress(uint64_t Address, + bool allowEmpty) const { + if (isAddressEncrypted(Address)) + return "#EncryptedString#"; + + for (const SectionRef &Section : OwningBinary->sections()) { + uint64_t SectAddress = Section.getAddress(); + uint64_t SectSize = Section.getSize(); + if (SectSize == 0) + continue; + if (Address >= SectAddress && Address < SectAddress + SectSize) { + uint64_t offset = Address - SectAddress; + StringRef SectContents; + Section.getContents(SectContents); + StringRef Target = StringRef(SectContents.data() + offset); + if (Target.size() + offset >= SectSize) + return make_error( + "Data extends pass the end of the section", + object_error::parse_failed); + if (Target.empty() && !allowEmpty) + return make_error( + "Expect to read a none zero length string", + object_error::parse_failed); + + return Target; + } + } + return make_error("Pointer out of bound", + object_error::parse_failed); +} + +Expected +MachOMetadata::getSymbolFromAddress(uint64_t Address, uint64_t &offset) const { + // Reset offset. + offset = 0; + + // Found the section. + SectionRef S; + uint64_t SectOffset = 0; + for (const SectionRef &Section : OwningBinary->sections()) { + uint64_t SectAddress = Section.getAddress(); + uint64_t SectSize = Section.getSize(); + if (SectSize == 0) + continue; + if (Address >= SectAddress && Address < SectAddress + SectSize) { + SectOffset = Address - SectAddress; + S = Section; + break; + } + } + if (S == SectionRef()) + return make_error("Address not in range of the macho file", + object_error::parse_failed); + + // See if there is an external relocation entry at the sect_offset. + bool reloc_found = false; + DataRefImpl Rel; + MachO::any_relocation_info RE; + bool isExtern = false; + SymbolRef Symbol; + for (const RelocationRef &Reloc : S.relocations()) { + uint64_t RelocOffset = Reloc.getOffset(); + if (RelocOffset == SectOffset) { + Rel = Reloc.getRawDataRefImpl(); + RE = OwningBinary->getRelocation(Rel); + if (OwningBinary->isRelocationScattered(RE)) + continue; + isExtern = OwningBinary->getPlainRelocationExternal(RE); + if (isExtern) { + symbol_iterator RelocSym = Reloc.getSymbol(); + Symbol = *RelocSym; + } + reloc_found = true; + break; + } + } + // If there is an external relocation entry for a symbol in this section + // at this section_offset then use that symbol's value for the n_value + // and return its name. + const char *SymbolName = nullptr; + if (reloc_found && isExtern) { + offset = Symbol.getValue(); + Expected NameOrError = Symbol.getName(); + if (!NameOrError) { + return NameOrError.takeError(); + } + StringRef Name = *NameOrError; + if (!Name.empty()) { + SymbolName = Name.data(); + return SymbolName; + } + } + return StringRef(); +} + +Expected +MachOMetadata::getResolvedValueFromAddress64(uint64_t Address) const { + uint64_t ptr; + if (auto p = getValueFromAddress(Address, sizeof(ptr))) + memcpy(&ptr, p->data(), sizeof(ptr)); + else + return p.takeError(); + if (OwningBinary->isLittleEndian() != sys::IsLittleEndianHost) + sys::swapByteOrder(ptr); + uint64_t offset; + if (auto symbol = getSymbolFromAddress(Address, offset)) + ptr += offset; + else + return symbol.takeError(); + return ptr; +} + +Expected +MachOMetadata::getResolvedValueFromAddress32(uint64_t Address) const { + uint32_t ptr; + if (auto p = getValueFromAddress(Address, sizeof(ptr))) + memcpy(&ptr, p->data(), sizeof(ptr)); + else + return p.takeError(); + if (OwningBinary->isLittleEndian() != sys::IsLittleEndianHost) + sys::swapByteOrder(ptr); + uint64_t offset; + if (auto symbol = getSymbolFromAddress(Address, offset)) + ptr += offset; + else + return symbol.takeError(); + return ptr; +} + +Expected MachOMetadata::getObjC2ClassRO64(ObjCClass Data) const { + assert(isObjC2() && OwningBinary->is64Bit() && + "Only support 64 bit Obj2 runtime"); + if (auto class_ro = getResolvedValueFromAddress64( + Data.getRawContent().getValue() + offsetof(class64_t, data))) { + return *class_ro & ~0x7; + } else + return class_ro.takeError(); +} + +Expected MachOMetadata::getObjC2ClassRO32(ObjCClass Data) const { + assert(isObjC2() && !OwningBinary->is64Bit() && + "Only support 32 bit Obj2 runtime"); + if (auto class_ro = getResolvedValueFromAddress32( + Data.getRawContent().getValue() + offsetof(class32_t, data))) { + return *class_ro & ~0x3; + } else + return class_ro.takeError(); +} + +Expected MachOMetadata::getObjC2MetaClass64(ObjCClass Data) const { + assert(isObjC2() && OwningBinary->is64Bit() && + "Only support 64 bit Obj2 runtime"); + if (auto isa = getResolvedValueFromAddress64(Data.getRawContent().getValue() + + offsetof(class64_t, isa))) { + if (*isa) { + return ObjCClass(this, *isa); + } else + return ObjCClass(this); + } else + return isa.takeError(); +} + +Expected MachOMetadata::getObjC2MetaClass32(ObjCClass Data) const { + assert(isObjC2() && !OwningBinary->is64Bit() && + "Only support 32 bit Obj2 runtime"); + if (auto isa = getResolvedValueFromAddress32(Data.getRawContent().getValue() + + offsetof(class32_t, isa))) { + if (*isa) { + return ObjCClass(this, *isa); + } else + return ObjCClass(this); + } else + return isa.takeError(); +} diff --git a/llvm/lib/ObjCMetadata/ObjCMetadata.cpp b/llvm/lib/ObjCMetadata/ObjCMetadata.cpp new file mode 100644 index 00000000000..a692e1273cf --- /dev/null +++ b/llvm/lib/ObjCMetadata/ObjCMetadata.cpp @@ -0,0 +1,177 @@ +//===- ObjCMetadata.cpp - ObjC Metadata Reader ------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the classes needed to parse ObjCMetadata. +// +//===----------------------------------------------------------------------===// +#include "llvm/ObjCMetadata/ObjCMetadata.h" + +using namespace llvm; + +Expected ObjCClassRef::getObjCClass() const { + return MetadataReader->getObjCClassFromRef(*this); +} + +Expected ObjCCategoryRef::getObjCCategory() const { + return MetadataReader->getObjCCategoryFromRef(*this); +} + +Expected ObjCProtocolRef::getObjCProtocol() const { + return MetadataReader->getObjCProtocolFromRef(*this); +} + +Expected ObjCClass::getName() const { + return MetadataReader->getObjCClassName(*this); +} + +Expected ObjCClassRef::getClassName() const { + if (MetadataReader->isObjCClassExternal(*this)) { + StringRef symName = MetadataReader->getSymbolNameFromRef(Data); + return MetadataReader->guessClassNameBasedOnSymbol(symName); + } else if (auto class_t = getObjCClass()) + return class_t->getName(); + else + return class_t.takeError(); +} + +bool ObjCClassRef::isExternal() const { + return MetadataReader->isObjCClassExternal(*this); +} + +Expected ObjCClass::getSuperClassName() const { + return MetadataReader->getObjCSuperClassName(*this); +} + +Expected ObjCClass::isSwift() const { + return MetadataReader->isObjCClassSwift(*this); +} + +Expected ObjCClass::isMetaClass() const { + return MetadataReader->isObjCClassMetaClass(*this); +} + +Expected ObjCClass::properties() const { + return MetadataReader->materializePropertyList(*this); +} + +Expected ObjCClass::instanceMethods() const { + return MetadataReader->materializeInstanceMethodList(*this); +} + +Expected ObjCClass::classMethods() const { + return MetadataReader->materializeClassMethodList(*this); +} + +Expected ObjCCategory::getName() const { + return MetadataReader->getObjCCategoryName(*this); +} + +Expected ObjCCategory::getBaseClassName() const { + return MetadataReader->getObjCCategoryBaseClassName(*this); +} + +Expected ObjCCategory::properties() const { + return MetadataReader->materializePropertyList(*this); +} + +Expected ObjCCategory::instanceMethods() const { + return MetadataReader->materializeInstanceMethodList(*this); +} + +Expected ObjCCategory::classMethods() const { + return MetadataReader->materializeClassMethodList(*this); +} + +Expected ObjCProtocol::getName() const { + return MetadataReader->getObjCProtocolName(*this); +} + +Expected ObjCProtocol::properties() const { + return MetadataReader->materializePropertyList(*this); +} + +Expected ObjCProtocol::instanceMethods() const { + return MetadataReader->materializeInstanceMethodList(*this); +} + +Expected ObjCProtocol::classMethods() const { + return MetadataReader->materializeClassMethodList(*this); +} + +Expected ObjCProtocol::optionalInstanceMethods() const { + return MetadataReader->materializeOptionalInstanceMethodList(*this); +} + +Expected ObjCProtocol::optionalClassMethods() const { + return MetadataReader->materializeOptionalClassMethodList(*this); +} + +Expected ObjCProperty::getName() const { + return MetadataReader->getPropertyName(*this); +} + +Expected ObjCProperty::getAttribute() const { + return MetadataReader->getPropertyAttribute(*this); +} + +Expected ObjCProperty::getGetter() const { + auto Name = getName(); + if (!Name) + return Name.takeError(); + if (*Name == "#EncryptedString#") + return *Name; + auto Attr = getAttribute(); + if (!Attr) + return Attr.takeError(); + // Find getter attribute. + SmallVector Attrs; + Attr->split(Attrs, ','); + for (auto a : Attrs) { + if (a.startswith("G")) + return a.drop_front(1).str(); + } + // Otherwise returns property name. + return Name->str(); +} + +Expected ObjCProperty::getSetter() const { + auto Name = getName(); + if (!Name) + return Name.takeError(); + if (*Name == "#EncryptedString#") + return *Name; + auto Attr = getAttribute(); + if (!Attr) + return Attr.takeError(); + // Find setter attribute. + SmallVector Attrs; + Attr->split(Attrs, ','); + for (auto a : Attrs) { + if (a.startswith("S")) + return a.drop_front(1).str(); + else if (a.startswith("R")) + return std::string(); // Read-only property + } + // Otherwise returns property name. + std::string setter = + "set" + Name->substr(0, 1).upper() + Name->substr(1).str() + ":"; + return setter; +} + +Expected ObjCMethod::getName() const { + return MetadataReader->getMethodName(*this); +} + +Expected ObjCMethod::getType() const { + return MetadataReader->getMethodType(*this); +} + +Expected ObjCSelectorRef::getSelector() const { + return MetadataReader->getObjCSelectorName(*this); +} diff --git a/llvm/lib/ObjCMetadata/macho-obj.h b/llvm/lib/ObjCMetadata/macho-obj.h new file mode 100644 index 00000000000..93a32a55235 --- /dev/null +++ b/llvm/lib/ObjCMetadata/macho-obj.h @@ -0,0 +1,588 @@ +#ifndef LLVM_LIB_OBJCMETADATA_MACHO_OBJ_H +#define LLVM_LIB_OBJCMETADATA_MACHO_OBJ_H + +#include + +// These are structs in the Objective-C meta data and read to produce the +// comments for disassembly. While these are part of the ABI they are no +// public defintions. So the are here not in include/llvm/Support/MachO.h . + +// The cfstring object in a 64-bit Mach-O file. +struct cfstring64_t { + uint64_t isa; // class64_t * (64-bit pointer) + uint64_t flags; // flag bits + uint64_t characters; // char * (64-bit pointer) + uint64_t length; // number of non-NULL characters in above +}; + +// The class object in a 64-bit Mach-O file. +struct class64_t { + uint64_t isa; // class64_t * (64-bit pointer) + uint64_t superclass; // class64_t * (64-bit pointer) + uint64_t cache; // Cache (64-bit pointer) + uint64_t vtable; // IMP * (64-bit pointer) + uint64_t data; // class_ro64_t * (64-bit pointer) +}; + +struct 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; + uint32_t instanceSize; + uint32_t reserved; + uint64_t ivarLayout; // const uint8_t * (64-bit pointer) + uint64_t name; // const char * (64-bit pointer) + uint64_t baseMethods; // const method_list_t * (64-bit pointer) + uint64_t baseProtocols; // const protocol_list_t * (64-bit pointer) + uint64_t ivars; // const ivar_list_t * (64-bit pointer) + uint64_t weakIvarLayout; // const uint8_t * (64-bit pointer) + uint64_t baseProperties; // const struct objc_property_list (64-bit pointer) +}; + +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) { + llvm::sys::swapByteOrder(cfs.isa); + llvm::sys::swapByteOrder(cfs.flags); + llvm::sys::swapByteOrder(cfs.characters); + llvm::sys::swapByteOrder(cfs.length); +} + +inline void swapStruct(struct class64_t &c) { + llvm::sys::swapByteOrder(c.isa); + llvm::sys::swapByteOrder(c.superclass); + llvm::sys::swapByteOrder(c.cache); + llvm::sys::swapByteOrder(c.vtable); + llvm::sys::swapByteOrder(c.data); +} + +inline void swapStruct(struct class32_t &c) { + llvm::sys::swapByteOrder(c.isa); + llvm::sys::swapByteOrder(c.superclass); + llvm::sys::swapByteOrder(c.cache); + llvm::sys::swapByteOrder(c.vtable); + llvm::sys::swapByteOrder(c.data); +} + +inline void swapStruct(struct class_ro64_t &cro) { + llvm::sys::swapByteOrder(cro.flags); + llvm::sys::swapByteOrder(cro.instanceStart); + llvm::sys::swapByteOrder(cro.instanceSize); + llvm::sys::swapByteOrder(cro.reserved); + llvm::sys::swapByteOrder(cro.ivarLayout); + llvm::sys::swapByteOrder(cro.name); + llvm::sys::swapByteOrder(cro.baseMethods); + llvm::sys::swapByteOrder(cro.baseProtocols); + llvm::sys::swapByteOrder(cro.ivars); + llvm::sys::swapByteOrder(cro.weakIvarLayout); + llvm::sys::swapByteOrder(cro.baseProperties); +} + +inline void swapStruct(struct class_ro32_t &cro) { + llvm::sys::swapByteOrder(cro.flags); + llvm::sys::swapByteOrder(cro.instanceStart); + llvm::sys::swapByteOrder(cro.instanceSize); + llvm::sys::swapByteOrder(cro.ivarLayout); + llvm::sys::swapByteOrder(cro.name); + llvm::sys::swapByteOrder(cro.baseMethods); + llvm::sys::swapByteOrder(cro.baseProtocols); + llvm::sys::swapByteOrder(cro.ivars); + llvm::sys::swapByteOrder(cro.weakIvarLayout); + llvm::sys::swapByteOrder(cro.baseProperties); +} + +inline void swapStruct(struct method_list64_t &ml) { + llvm::sys::swapByteOrder(ml.entsize); + llvm::sys::swapByteOrder(ml.count); +} + +inline void swapStruct(struct method_list32_t &ml) { + llvm::sys::swapByteOrder(ml.entsize); + llvm::sys::swapByteOrder(ml.count); +} + +inline void swapStruct(struct method64_t &m) { + llvm::sys::swapByteOrder(m.name); + llvm::sys::swapByteOrder(m.types); + llvm::sys::swapByteOrder(m.imp); +} + +inline void swapStruct(struct method32_t &m) { + llvm::sys::swapByteOrder(m.name); + llvm::sys::swapByteOrder(m.types); + llvm::sys::swapByteOrder(m.imp); +} + +inline void swapStruct(struct protocol_list64_t &pl) { + llvm::sys::swapByteOrder(pl.count); +} + +inline void swapStruct(struct protocol_list32_t &pl) { + llvm::sys::swapByteOrder(pl.count); +} + +inline void swapStruct(struct protocol64_t &p) { + llvm::sys::swapByteOrder(p.isa); + llvm::sys::swapByteOrder(p.name); + llvm::sys::swapByteOrder(p.protocols); + llvm::sys::swapByteOrder(p.instanceMethods); + llvm::sys::swapByteOrder(p.classMethods); + llvm::sys::swapByteOrder(p.optionalInstanceMethods); + llvm::sys::swapByteOrder(p.optionalClassMethods); + llvm::sys::swapByteOrder(p.instanceProperties); +} + +inline void swapStruct(struct protocol32_t &p) { + llvm::sys::swapByteOrder(p.isa); + llvm::sys::swapByteOrder(p.name); + llvm::sys::swapByteOrder(p.protocols); + llvm::sys::swapByteOrder(p.instanceMethods); + llvm::sys::swapByteOrder(p.classMethods); + llvm::sys::swapByteOrder(p.optionalInstanceMethods); + llvm::sys::swapByteOrder(p.optionalClassMethods); + llvm::sys::swapByteOrder(p.instanceProperties); +} + +inline void swapStruct(struct ivar_list64_t &il) { + llvm::sys::swapByteOrder(il.entsize); + llvm::sys::swapByteOrder(il.count); +} + +inline void swapStruct(struct ivar_list32_t &il) { + llvm::sys::swapByteOrder(il.entsize); + llvm::sys::swapByteOrder(il.count); +} + +inline void swapStruct(struct ivar64_t &i) { + llvm::sys::swapByteOrder(i.offset); + llvm::sys::swapByteOrder(i.name); + llvm::sys::swapByteOrder(i.type); + llvm::sys::swapByteOrder(i.alignment); + llvm::sys::swapByteOrder(i.size); +} + +inline void swapStruct(struct ivar32_t &i) { + llvm::sys::swapByteOrder(i.offset); + llvm::sys::swapByteOrder(i.name); + llvm::sys::swapByteOrder(i.type); + llvm::sys::swapByteOrder(i.alignment); + llvm::sys::swapByteOrder(i.size); +} + +inline void swapStruct(struct objc_property_list64 &pl) { + llvm::sys::swapByteOrder(pl.entsize); + llvm::sys::swapByteOrder(pl.count); +} + +inline void swapStruct(struct objc_property_list32 &pl) { + llvm::sys::swapByteOrder(pl.entsize); + llvm::sys::swapByteOrder(pl.count); +} + +inline void swapStruct(struct objc_property64 &op) { + llvm::sys::swapByteOrder(op.name); + llvm::sys::swapByteOrder(op.attributes); +} + +inline void swapStruct(struct objc_property32 &op) { + llvm::sys::swapByteOrder(op.name); + llvm::sys::swapByteOrder(op.attributes); +} + +inline void swapStruct(struct category64_t &c) { + llvm::sys::swapByteOrder(c.name); + llvm::sys::swapByteOrder(c.cls); + llvm::sys::swapByteOrder(c.instanceMethods); + llvm::sys::swapByteOrder(c.classMethods); + llvm::sys::swapByteOrder(c.protocols); + llvm::sys::swapByteOrder(c.instanceProperties); +} + +inline void swapStruct(struct category32_t &c) { + llvm::sys::swapByteOrder(c.name); + llvm::sys::swapByteOrder(c.cls); + llvm::sys::swapByteOrder(c.instanceMethods); + llvm::sys::swapByteOrder(c.classMethods); + llvm::sys::swapByteOrder(c.protocols); + llvm::sys::swapByteOrder(c.instanceProperties); +} + +inline void swapStruct(struct objc_image_info64 &o) { + llvm::sys::swapByteOrder(o.version); + llvm::sys::swapByteOrder(o.flags); +} + +inline void swapStruct(struct objc_image_info32 &o) { + llvm::sys::swapByteOrder(o.version); + llvm::sys::swapByteOrder(o.flags); +} + +inline void swapStruct(struct imageInfo_t &o) { + llvm::sys::swapByteOrder(o.version); + llvm::sys::swapByteOrder(o.flags); +} + +inline void swapStruct(struct message_ref64 &mr) { + llvm::sys::swapByteOrder(mr.imp); + llvm::sys::swapByteOrder(mr.sel); +} + +inline void swapStruct(struct message_ref32 &mr) { + llvm::sys::swapByteOrder(mr.imp); + llvm::sys::swapByteOrder(mr.sel); +} + +inline void swapStruct(struct objc_module_t &module) { + llvm::sys::swapByteOrder(module.version); + llvm::sys::swapByteOrder(module.size); + llvm::sys::swapByteOrder(module.name); + llvm::sys::swapByteOrder(module.symtab); +} + +inline void swapStruct(struct objc_symtab_t &symtab) { + llvm::sys::swapByteOrder(symtab.sel_ref_cnt); + llvm::sys::swapByteOrder(symtab.refs); + llvm::sys::swapByteOrder(symtab.cls_def_cnt); + llvm::sys::swapByteOrder(symtab.cat_def_cnt); +} + +inline void swapStruct(struct objc_class_t &objc_class) { + llvm::sys::swapByteOrder(objc_class.isa); + llvm::sys::swapByteOrder(objc_class.super_class); + llvm::sys::swapByteOrder(objc_class.name); + llvm::sys::swapByteOrder(objc_class.version); + llvm::sys::swapByteOrder(objc_class.info); + llvm::sys::swapByteOrder(objc_class.instance_size); + llvm::sys::swapByteOrder(objc_class.ivars); + llvm::sys::swapByteOrder(objc_class.methodLists); + llvm::sys::swapByteOrder(objc_class.cache); + llvm::sys::swapByteOrder(objc_class.protocols); +} + +inline void swapStruct(struct objc_category_t &objc_category) { + llvm::sys::swapByteOrder(objc_category.category_name); + llvm::sys::swapByteOrder(objc_category.class_name); + llvm::sys::swapByteOrder(objc_category.instance_methods); + llvm::sys::swapByteOrder(objc_category.class_methods); + llvm::sys::swapByteOrder(objc_category.protocols); +} + +inline void swapStruct(struct objc_ivar_list_t &objc_ivar_list) { + llvm::sys::swapByteOrder(objc_ivar_list.ivar_count); +} + +inline void swapStruct(struct objc_ivar_t &objc_ivar) { + llvm::sys::swapByteOrder(objc_ivar.ivar_name); + llvm::sys::swapByteOrder(objc_ivar.ivar_type); + llvm::sys::swapByteOrder(objc_ivar.ivar_offset); +} + +inline void swapStruct(struct objc_method_list_t &method_list) { + llvm::sys::swapByteOrder(method_list.obsolete); + llvm::sys::swapByteOrder(method_list.method_count); +} + +inline void swapStruct(struct objc_method_t &method) { + llvm::sys::swapByteOrder(method.method_name); + llvm::sys::swapByteOrder(method.method_types); + llvm::sys::swapByteOrder(method.method_imp); +} + +inline void swapStruct(struct objc_protocol_list_t &protocol_list) { + llvm::sys::swapByteOrder(protocol_list.next); + llvm::sys::swapByteOrder(protocol_list.count); +} + +inline void swapStruct(struct objc_protocol_t &protocol) { + llvm::sys::swapByteOrder(protocol.isa); + llvm::sys::swapByteOrder(protocol.protocol_name); + llvm::sys::swapByteOrder(protocol.protocol_list); + llvm::sys::swapByteOrder(protocol.instance_methods); + llvm::sys::swapByteOrder(protocol.class_methods); +} + +inline void swapStruct(struct objc_method_description_list_t &mdl) { + llvm::sys::swapByteOrder(mdl.count); +} + +inline void swapStruct(struct objc_method_description_t &md) { + llvm::sys::swapByteOrder(md.name); + llvm::sys::swapByteOrder(md.types); +} + +#endif