Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SharedCache] Fix handling of relative selectors in macOS shared caches #6192

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions view/sharedcache/core/DSCView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,15 @@ bool DSCView::Init()
"\t\tuint64_t rosettaReadWriteSize;\t// maximum size of the Rosetta read-write region\n"
"\t\tuint32_t imagesOffset;\t\t\t// file offset to first dyld_cache_image_info\n"
"\t\tuint32_t imagesCount;\t\t\t// number of dyld_cache_image_info entries\n"
"\t\tuint32_t cacheSubType; // 0 for development, 1 for production, when cacheType is multi-cache(2)\n"
"\t\tuint64_t objcOptsOffset; // VM offset from cache_header* to ObjC optimizations header\n"
"\t\tuint64_t objcOptsSize; // size of ObjC optimizations header\n"
"\t\tuint64_t cacheAtlasOffset; // VM offset from cache_header* to embedded cache atlas for process introspection\n"
"\t\tuint64_t cacheAtlasSize; // size of embedded cache atlas\n"
"\t\tuint64_t dynamicDataOffset; // VM offset from cache_header* to the location of dyld_cache_dynamic_data_header\n"
"\t\tuint64_t dynamicDataMaxSize; // maximum size of space reserved from dynamic data\n"
"\t\tuint32_t tproMappingsOffset; // file offset to first dyld_cache_tpro_mapping_info\n"
"\t\tuint32_t tproMappingsCount; // number of dyld_cache_tpro_mapping_info entries\n"
"\t};", headerType, err);

Ref<Settings> settings = GetLoadSettings(GetTypeName());
Expand Down Expand Up @@ -732,8 +741,13 @@ bool DSCView::Init()
return false;
}

AddAutoSegment(primaryBase, 0x200, 0, 0x200, SegmentReadable);
AddAutoSection("__dsc_header", primaryBase, 0x200, ReadOnlyCodeSectionSemantics);
uint64_t headerSize = std::min(basePointer, headerType.type->GetWidth());
// Truncate the `dyld_cache_header` structure to the structure present in the cache file.
auto newStructure = StructureBuilder(headerType.type->GetStructure()).SetWidth(headerSize).Finalize();
headerType.type = TypeBuilder::StructureType(newStructure).Finalize();

AddAutoSegment(primaryBase, headerSize, 0, headerSize, SegmentReadable);
AddAutoSection("__dsc_header", primaryBase, headerSize, ReadOnlyDataSectionSemantics);
DefineType("dyld_cache_header", headerType.name, headerType.type);
DefineAutoSymbolAndVariableOrFunction(GetDefaultPlatform(), new Symbol(DataSymbol, "primary_cache_header", primaryBase), headerType.type);

Expand Down
116 changes: 64 additions & 52 deletions view/sharedcache/core/ObjC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -775,22 +775,57 @@ 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;
head.entsizeAndFlags = reader->Read32();
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++)
{
Expand All @@ -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
{
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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);
Expand Down
9 changes: 7 additions & 2 deletions view/sharedcache/core/ObjC.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ namespace DSCObjC {
typedef struct {
uint64_t count;
} protocol_list_t;
struct relative_list_list_entry_t {
uint64_t imageIndex: 16;
int64_t listOffset: 48;
};
typedef struct {
view_ptr_t isa;
view_ptr_t mangledName;
Expand Down Expand Up @@ -214,8 +218,9 @@ namespace DSCObjC {
std::vector<QualifiedNameOrType> ParseEncodedType(const std::string& type);
void DefineObjCSymbol(BNSymbolType symbolType, QualifiedName typeName, const std::string& name, uint64_t addr, bool deferred);
void DefineObjCSymbol(BNSymbolType symbolType, Ref<Type> type, const std::string& name, uint64_t addr, bool deferred);
void ReadIvarList(VMReader* reader, ClassBase& cls, std::string name, view_ptr_t start);
void ReadMethodList(VMReader* reader, ClassBase& cls, std::string name, view_ptr_t start);
void ReadIvarList(VMReader* reader, ClassBase& cls, std::string_view name, view_ptr_t start);
void ReadMethodList(VMReader* reader, ClassBase& cls, std::string_view name, view_ptr_t start);
void ReadListOfMethodLists(VMReader* reader, ClassBase& cls, std::string_view name, view_ptr_t start);
void LoadClasses(VMReader* reader, Ref<Section> listSection);
void LoadCategories(VMReader* reader, Ref<Section> listSection);
void LoadProtocols(VMReader* reader, Ref<Section> listSection);
Expand Down
Loading