diff options
| author | Mark Rowe <mrowe@bdash.net.nz> | 2024-11-24 22:28:04 -0800 |
|---|---|---|
| committer | Mark Rowe <mrowe@bdash.net.nz> | 2025-01-10 11:53:33 -0800 |
| commit | aa408cfdeaa3cd0c4319598223a4548b18731290 (patch) | |
| tree | e73da350192ff0ea942661c5850ee14a38accaaa /view/sharedcache/core/ObjC.cpp | |
| parent | a44b78355031ed429ac07f20a6439f07f9f47d9d (diff) | |
[SharedCache] Fix handling of relative selectors in macOS shared caches
Find the relative selector base address in the Objective-C optimization
data pointed to by the shared cache header, rather than via
`__objc_scoffs`. This is only present on iOS, and not for every iOS
version that encodes selectors via direct offsets.
This also includes some related improvements:
1. Direct selectors get their own pointer type so they're rendered
correctly in the view.
2. Method lists encoded as lists of lists are now handled.
3. The `dyld_cache_header` type added to the view is truncated to the
length in the loaded cache. This ensures it is applied to the view.
4. A couple of methods that process method IMPs and selectors are
updated to check whether the address is valid before attempting to
process them. They would otherwise fail by throwing an exception if
they proceed, but checking for validity is quicker and makes
exception breakpoints usable.
Diffstat (limited to 'view/sharedcache/core/ObjC.cpp')
| -rw-r--r-- | view/sharedcache/core/ObjC.cpp | 116 |
1 files changed, 64 insertions, 52 deletions
diff --git a/view/sharedcache/core/ObjC.cpp b/view/sharedcache/core/ObjC.cpp index 95fbb2a2..6e998e2a 100644 --- a/view/sharedcache/core/ObjC.cpp +++ b/view/sharedcache/core/ObjC.cpp @@ -775,7 +775,7 @@ void DSCObjCProcessor::LoadProtocols(VMReader* reader, Ref<Section> listSection) } } -void DSCObjCProcessor::ReadMethodList(VMReader* reader, ClassBase& cls, std::string name, view_ptr_t start) +void DSCObjCProcessor::ReadListOfMethodLists(VMReader* reader, ClassBase& cls, std::string_view name, view_ptr_t start) { reader->Seek(start); method_list_t head; @@ -783,14 +783,49 @@ void DSCObjCProcessor::ReadMethodList(VMReader* reader, ClassBase& cls, std::str head.count = reader->Read32(); if (head.count > 0x1000) { + m_logger->LogError("List of method lists at 0x%llx has an invalid count of 0x%x", start, head.count); + return; + } + + for (size_t i = 0; i < head.count; ++i) { + relative_list_list_entry_t list_entry; + reader->Read(&list_entry, sizeof(list_entry)); + + ReadMethodList(reader, cls, name, reader->GetOffset() - sizeof(list_entry) + list_entry.listOffset); + // Reset the cursor to immediately past the list entry. + reader->Seek(start + sizeof(method_list_t) + ((i + 1) * sizeof(relative_list_list_entry_t))); + } +} + +void DSCObjCProcessor::ReadMethodList(VMReader* reader, ClassBase& cls, std::string_view name, view_ptr_t start) +{ + // Lower two bits indicate the type of method list. + switch (start & 0b11) { + case 0: + break; + case 1: + return ReadListOfMethodLists(reader, cls, name, start - 1); + default: + m_logger->LogDebug("ReadMethodList: Unknown method list type at 0x%llx: %d", start, start & 0x3); + return; + } + + reader->Seek(start); + method_list_t head; + head.entsizeAndFlags = reader->Read32(); + head.count = reader->Read32(); + + if (head.count > 0x1000) + { m_logger->LogError("Method list at 0x%llx has an invalid count of 0x%x", start, head.count); return; } + uint64_t pointerSize = m_data->GetAddressSize(); bool relativeOffsets = (head.entsizeAndFlags & 0xFFFF0000) & 0x80000000; bool directSelectors = (head.entsizeAndFlags & 0xFFFF0000) & 0x40000000; auto methodSize = relativeOffsets ? 12 : pointerSize * 3; - DefineObjCSymbol(DataSymbol, m_typeNames.methodList, "method_list_" + name, start, true); + DefineObjCSymbol(DataSymbol, m_typeNames.methodList, "method_list_" + std::string(name), start, true); for (unsigned i = 0; i < head.count; i++) { @@ -806,18 +841,14 @@ void DSCObjCProcessor::ReadMethodList(VMReader* reader, ClassBase& cls, std::str // -- if (relativeOffsets) { - if (m_customRelativeMethodSelectorBase.has_value()) - { - meth.name = m_customRelativeMethodSelectorBase.value() + reader->ReadS32(); - meth.types = reader->GetOffset() + reader->ReadS32(); - meth.imp = reader->GetOffset() + reader->ReadS32(); - } - else - { - meth.name = reader->GetOffset() + reader->ReadS32(); - meth.types = reader->GetOffset() + reader->ReadS32(); - meth.imp = reader->GetOffset() + reader->ReadS32(); + auto selectorBaseOffset = reader->GetOffset(); + if (directSelectors && m_customRelativeMethodSelectorBase.has_value()) { + selectorBaseOffset = m_customRelativeMethodSelectorBase.value(); } + + meth.name = selectorBaseOffset + reader->Read32(); + meth.types = reader->GetOffset() + reader->ReadS32(); + meth.imp = reader->GetOffset() + reader->ReadS32(); } else { @@ -881,14 +912,14 @@ void DSCObjCProcessor::ReadMethodList(VMReader* reader, ClassBase& cls, std::str } } -void DSCObjCProcessor::ReadIvarList(VMReader* reader, ClassBase& cls, std::string name, view_ptr_t start) +void DSCObjCProcessor::ReadIvarList(VMReader* reader, ClassBase& cls, std::string_view name, view_ptr_t start) { reader->Seek(start); ivar_list_t head; head.entsizeAndFlags = reader->Read32(); head.count = reader->Read32(); auto addressSize = m_data->GetAddressSize(); - DefineObjCSymbol(DataSymbol, m_typeNames.ivarList, "ivar_list_" + name, start, true); + DefineObjCSymbol(DataSymbol, m_typeNames.ivarList, "ivar_list_" + std::string(name), start, true); if (head.count > 0x1000) { m_logger->LogError("Ivar list at 0x%llx has an invalid count of 0x%llx", start, head.count); @@ -1010,6 +1041,10 @@ void DSCObjCProcessor::GenerateClassTypes() bool DSCObjCProcessor::ApplyMethodType(Class& cls, Method& method, bool isInstanceMethod) { + if (!method.imp || !m_data->IsValidOffset(method.imp)) { + return false; + } + std::stringstream r(method.name); std::string token; @@ -1221,6 +1256,19 @@ void DSCObjCProcessor::ProcessObjCData(std::shared_ptr<VM> vm, std::string baseN m_typeNames.nsuInteger = defineTypedef(m_data, {"NSUInteger"}, Type::IntegerType(addrSize, false)); m_typeNames.cgFloat = defineTypedef(m_data, {"CGFloat"}, Type::FloatType(addrSize)); + Ref<Type> relativeSelectorPtr; + auto reader = VMReader(vm); + if (auto objCRelativeMethodsBaseAddr = m_cache->GetObjCRelativeMethodBaseAddress(reader)) { + m_logger->LogDebug("RelativeMethodSelector Base: 0x%llx", objCRelativeMethodsBaseAddr); + m_customRelativeMethodSelectorBase = objCRelativeMethodsBaseAddr; + + auto type = TypeBuilder::PointerType(4, Type::PointerType(addrSize, Type::IntegerType(1, false))) + .SetPointerBase(RelativeToConstantPointerBaseType, objCRelativeMethodsBaseAddr) + .Finalize(); + auto relativeSelectorPtrName = defineTypedef(m_data, {"relative_SEL"}, type); + relativeSelectorPtr = Type::NamedType(m_data, relativeSelectorPtrName); + } + // https://github.com/apple-oss-distributions/objc4/blob/196363c165b175ed925ef6b9b99f558717923c47/runtime/objc-abi.h EnumerationBuilder imageInfoFlagBuilder; imageInfoFlagBuilder.AddMemberWithValue("IsReplacement", 1 << 0); @@ -1256,7 +1304,7 @@ void DSCObjCProcessor::ProcessObjCData(std::shared_ptr<VM> vm, std::string baseN m_typeNames.imageInfo = imageInfoType.first; StructureBuilder methodEntry; - methodEntry.AddMember(rptr_t, "name"); + methodEntry.AddMember(relativeSelectorPtr ? relativeSelectorPtr : rptr_t, "name"); methodEntry.AddMember(rptr_t, "types"); methodEntry.AddMember(rptr_t, "imp"); auto type = finalizeStructureBuilder(m_data, methodEntry, "objc_method_entry_t"); @@ -1360,42 +1408,6 @@ void DSCObjCProcessor::ProcessObjCData(std::shared_ptr<VM> vm, std::string baseN protocolBuilder.AddMember(Type::IntegerType(4, false), "flags"); m_typeNames.protocol = finalizeStructureBuilder(m_data, protocolBuilder, "objc_protocol_t").first; - auto reader = VMReader(vm); - - if (auto addr = m_cache->GetImageStart("/usr/lib/libobjc.A.dylib")) - { - auto header = m_cache->HeaderForAddress(addr.value()); - uint64_t scoffs_addr = 0; - size_t scoffs_size = 0; - - for (const auto& section : header->sections) - { - char name[17]; - memcpy(name, section.sectname, 16); - name[16] = 0; - if (std::string(name) == "__objc_scoffs") - { - scoffs_addr = section.addr; - scoffs_size = section.size; - break; - } - } - - if (scoffs_size && scoffs_addr) - { - if (scoffs_size == 0x20) - { - m_customRelativeMethodSelectorBase = reader.ReadULong(scoffs_addr); - } - else - { - m_customRelativeMethodSelectorBase = reader.ReadULong(scoffs_addr + 8); - } - m_logger->LogDebug("RelativeMethodSelector Base: 0x%llx", m_customRelativeMethodSelectorBase.value()); - } - } - - m_data->BeginBulkModifySymbols(); if (auto classList = m_data->GetSectionByName(baseName + "::__objc_classlist")) LoadClasses(&reader, classList); |
