ObjCMachOBinary.cpp [plain text]
#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<ObjCClassList> MachOMetadata::classes() const {
if (isObjC1())
return make_error<StringError>("Unsupported ObjC Runtime",
object_error::parse_failed);
ObjCClassList ClassList;
SectionRef ClassSect = getSection("__OBJC2", "__class_list");
if (ClassSect == SectionRef())
ClassSect = getSection("__DATA", "__objc_classlist");
if (ClassSect == SectionRef())
return ClassList;
if (ClassSect.getSize() % OwningBinary->getBytesInAddress() != 0)
return make_error<StringError>(
"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<ObjCClass> 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 {
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 {
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 {
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<ObjCCategoryList> MachOMetadata::categories() const {
if (isObjC1())
return make_error<StringError>("Unsupported ObjC Runtime",
object_error::parse_failed);
ObjCCategoryList CategoryList;
SectionRef CatSect = getSection("__OBJC2", "__category_list");
if (CatSect == SectionRef())
CatSect = getSection("__DATA", "__objc_catlist");
if (CatSect == SectionRef())
return CategoryList;
if (CatSect.getSize() % OwningBinary->getBytesInAddress() != 0)
return make_error<StringError>(
"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<ObjCCategory>
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<ObjCProtocolList> MachOMetadata::protocols() const {
if (isObjC1())
return make_error<StringError>("Unsupported ObjC Runtime",
object_error::parse_failed);
ObjCProtocolList ProtocolList;
SectionRef ProtoSect = getSection("__OBJC2", "__protocol_list");
if (ProtoSect == SectionRef())
ProtoSect = getSection("__DATA", "__objc_protolist");
if (ProtoSect == SectionRef())
return ProtocolList;
if (ProtoSect.getSize() % OwningBinary->getBytesInAddress() != 0)
return make_error<StringError>(
"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<ObjCProtocol>
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<StringRef> MachOMetadata::getObjCClassName(ObjCClass ClassRef) const {
if (isObjC1())
return make_error<StringError>("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<StringRef> MachOMetadata::getObjCSuperClassName(ObjCClass Data) const {
if (isObjC1())
return make_error<StringError>("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<bool> 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<bool> 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<ObjCPropertyList>
MachOMetadata::materializePropertyList(ObjCClass ClassRef) const {
if (isObjC1())
return make_error<StringError>("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<StringRef> MachOMetadata::getPropertyName(ObjCProperty Data) const {
if (isObjC1())
return make_error<StringError>("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<StringRef>
MachOMetadata::getPropertyAttribute(ObjCProperty Data) const {
if (isObjC1())
return make_error<StringError>("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<ObjCMethodList>
MachOMetadata::materializeInstanceMethodList(ObjCClass ClassRef) const {
if (isObjC1())
return make_error<StringError>("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<ObjCMethodList>
MachOMetadata::materializeClassMethodList(ObjCClass ClassRef) const {
if (isObjC1())
return make_error<StringError>("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<StringRef> MachOMetadata::getObjCCategoryName(ObjCCategory Cat) const {
if (isObjC1())
return make_error<StringError>("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<StringRef>
MachOMetadata::getObjCCategoryBaseClassName(ObjCCategory Cat) const {
if (isObjC1())
return make_error<StringError>("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<ObjCPropertyList>
MachOMetadata::materializePropertyList(ObjCCategory Data) const {
if (isObjC1())
return make_error<StringError>("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<ObjCMethodList>
MachOMetadata::materializeInstanceMethodList(ObjCCategory Data) const {
if (isObjC1())
return make_error<StringError>("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<ObjCMethodList>
MachOMetadata::materializeClassMethodList(ObjCCategory Data) const {
if (isObjC1())
return make_error<StringError>("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<StringRef>
MachOMetadata::getObjCProtocolName(ObjCProtocol Data) const {
if (isObjC1())
return make_error<StringError>("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<ObjCPropertyList>
MachOMetadata::materializePropertyList(ObjCProtocol Data) const {
if (isObjC1())
return make_error<StringError>("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<ObjCMethodList>
MachOMetadata::materializeInstanceMethodList(ObjCProtocol Data) const {
if (isObjC1())
return make_error<StringError>("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<ObjCMethodList>
MachOMetadata::materializeClassMethodList(ObjCProtocol Data) const {
if (isObjC1())
return make_error<StringError>("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<ObjCMethodList>
MachOMetadata::materializeOptionalInstanceMethodList(ObjCProtocol Data) const {
if (isObjC1())
return make_error<StringError>("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<ObjCMethodList>
MachOMetadata::materializeOptionalClassMethodList(ObjCProtocol Data) const {
if (isObjC1())
return make_error<StringError>("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<StringRef> MachOMetadata::getMethodName(ObjCMethod Data) const {
if (isObjC1())
return make_error<StringError>("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<StringRef> MachOMetadata::getMethodType(ObjCMethod Data) const {
if (isObjC1())
return make_error<StringError>("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<ObjCSelectorList> MachOMetadata::referencedSelectors() const {
if (isObjC1())
return make_error<StringError>("Unsupported ObjC Runtime",
object_error::parse_failed);
ObjCSelectorList SelectorList;
SectionRef SelSect = getSection("__DATA", "__objc_selrefs");
if (SelSect == SectionRef())
return SelectorList;
if (SelSect.getSize() % OwningBinary->getBytesInAddress() != 0)
return make_error<StringError>(
"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<StringRef>
MachOMetadata::getObjCSelectorName(ObjCSelectorRef Ref) const {
if (isObjC1())
return make_error<StringError>("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 {
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; });
};
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<StringRef> MachOMetadata::getValueFromAddress(uint64_t Address,
uint64_t size,
bool objc_only) const {
if (isAddressEncrypted(Address))
return make_error<StringError>("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);
StringRef Target = (size == 0)
? StringRef(SectContents.data() + offset)
: StringRef(SectContents.data() + offset, size);
if (Target.size() + offset > SectSize)
return make_error<StringError>(
"Data extends pass the end of the section",
object_error::parse_failed);
return Target;
}
}
return make_error<StringError>("Pointer out of bound",
object_error::parse_failed);
}
bool MachOMetadata::isAddressEncrypted(uint64_t Address) const {
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<StringRef> 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<StringError>(
"Data extends pass the end of the section",
object_error::parse_failed);
if (Target.empty() && !allowEmpty)
return make_error<StringError>(
"Expect to read a none zero length string",
object_error::parse_failed);
return Target;
}
}
return make_error<StringError>("Pointer out of bound",
object_error::parse_failed);
}
Expected<StringRef>
MachOMetadata::getSymbolFromAddress(uint64_t Address, uint64_t &offset) const {
offset = 0;
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<StringError>("Address not in range of the macho file",
object_error::parse_failed);
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;
}
}
const char *SymbolName = nullptr;
if (reloc_found && isExtern) {
offset = Symbol.getValue();
ErrorOr<StringRef> NameOrError = Symbol.getName();
if (!NameOrError) {
return errorOrToExpected(std::move(NameOrError));
}
StringRef Name = *NameOrError;
if (!Name.empty()) {
SymbolName = Name.data();
return SymbolName;
}
}
return StringRef();
}
Expected<uint64_t>
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<uint32_t>
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<ObjCRef> 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<ObjCRef> 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<ObjCClass> 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<ObjCClass> 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();
}