Source code for umbrella.objc.structs

# Copyright (c) 2023 MatrixEditor
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations

import dataclasses as dc
import typing as t

import construct as cs
import construct_dataclasses as csd

from umbrella.runtime import Virtual, uint32_t, uint64_t, int32_t, uintptr_t, uint16_t
from umbrella.objc import Pointer64, Pointer32


[docs] @dc.dataclass class TargetMethodRaw64(Virtual): """Objective-C method description.""" #: The name of this method. name: uintptr_t = csd.csfield(Pointer64) #: The types of this method. signature: uintptr_t = csd.csfield(Pointer64) #: The absolute implementation address impl: uintptr_t = csd.csfield(Pointer64)
@dc.dataclass class TargetMethodRaw32(Virtual): # The name of this method. name: uintptr_t = csd.csfield(Pointer32) # The types of this method. signature: uintptr_t = csd.csfield(Pointer32) impl: uintptr_t = csd.csfield(Pointer32) @dc.dataclass class TargetSmallMethodRaw(Virtual): # The name of this method. name: int32_t = csd.csfield(cs.Int32sl) # The types of this method. signature: int32_t = csd.csfield(cs.Int32sl) impl: int32_t = csd.csfield(cs.Int32sl)
[docs] @dc.dataclass class TargetIVarRaw64(Virtual): """Raw IVar structure.""" #: offset was originally 64-bit on some x86_64 platforms. #: We read and write only 32 bits of it. Some metadata provides all 64 #: bits. This is harmless for unsigned little-endian values. offset: int32_t = csd.csfield(cs.Int32sl) remainder: uint32_t = csd.csfield(cs.Int32ul) #: unused (only 64-bit) name: uintptr_t = csd.csfield(Pointer64) #: the name of this ivar type: uintptr_t = csd.csfield(Pointer64) #: its type alignment: uint32_t = csd.csfield(cs.Int32ul) #: the internal alignment size: uint32_t = csd.csfield(cs.Int32ul) #: the internal ivar size in memory
@dc.dataclass class TargetIVarRaw32(Virtual): offset: int32_t = csd.csfield(cs.Int32sl) name: uintptr_t = csd.csfield(Pointer32) type: uintptr_t = csd.csfield(Pointer32) alignment: uint32_t = csd.csfield(cs.Int32ul) size: uint32_t = csd.csfield(cs.Int32ul)
[docs] @dc.dataclass class TargetPropertyRaw64(Virtual): """Raw layout of an Objective-C property.""" name: uintptr_t = csd.csfield(Pointer64) #: the property's name (reference) attributes: uintptr_t = csd.csfield(Pointer64) #: its attributes (reference)
@dc.dataclass class TargetPropertyRaw32(Virtual): name: uintptr_t = csd.csfield(Pointer32) attributes: uintptr_t = csd.csfield(Pointer32) @dc.dataclass class TargetGenericList(Virtual): # high bits used for fixup markers entsize_and_flags: uint32_t = csd.csfield(cs.Int32ul) count: uint32_t = csd.csfield(cs.Int32ul) def get_mask(self) -> int: return getattr(self, "mask", 0) def entsize(self) -> int: return self.entsize_and_flags & ~self.get_mask() def flags(self) -> int: return self.entsize_and_flags & self.get_mask() @dc.dataclass class TargetMethodList(TargetGenericList): mask: int = csd.csfield(cs.Computed(lambda _: 0xFFFF0003)) TargetIVarList = TargetGenericList TargetPropertyList = TargetGenericList
[docs] @dc.dataclass class TargetObjCObjectRaw64(Virtual): """Base struct for all definitions that may store a class definition.""" isa_storage: uintptr_t = csd.csfield(Pointer64) """Class storage or pointer"""
@dc.dataclass class TargetObjCObjectRaw32(Virtual): # Class storage or pointer isa_storage: uintptr_t = csd.csfield(Pointer32)
[docs] @dc.dataclass class TargetProtocolRaw64(TargetObjCObjectRaw64): """Raw protocol struct.""" #: the protocol's name (reference) name: uintptr_t = csd.csfield(Pointer64) #: a reference to a list of protocols this one conforms to protocols: uintptr_t = csd.csfield(Pointer64) #: a list of required instance methods (list reference) required_instance_methods: uintptr_t = csd.csfield(Pointer64) #: a list of required class methods (list reference) required_class_methods: uintptr_t = csd.csfield(Pointer64) #: a list of optional instance methods (list reference) optional_instance_methods: uintptr_t = csd.csfield(Pointer64) #: a list of optional class methods (list reference) optional_class_methods: uintptr_t = csd.csfield(Pointer64) #: all defined instance properties (list reference) instance_properties: uintptr_t = csd.csfield(Pointer64) #: the size of this struct size: uint32_t = csd.csfield(cs.Int32ul) # sizeof(protocol_t) #: additional flags for this protocol flags: uint32_t = csd.csfield(cs.Int32ul)
@dc.dataclass class TargetProtocolRaw32(TargetObjCObjectRaw32): name: uintptr_t = csd.csfield(Pointer32) protocols: uintptr_t = csd.csfield(Pointer32) required_instance_methods: uintptr_t = csd.csfield(Pointer32) required_class_methods: uintptr_t = csd.csfield(Pointer32) optional_instance_methods: uintptr_t = csd.csfield(Pointer32) optional_class_methods: uintptr_t = csd.csfield(Pointer32) instance_properties: uintptr_t = csd.csfield(Pointer32) size: uint32_t = csd.csfield(cs.Int32ul) # sizeof(protocol_t) flags: uint32_t = csd.csfield(cs.Int32ul) @dc.dataclass class TargetProtocolList64(Virtual): #: According to Apple's source code: #: - "count is pointer-sized by accident." count: uintptr_t = csd.csfield(Pointer64) @dc.dataclass class TargetProtocolList32(Virtual): count: uintptr_t = csd.csfield(Pointer32) class TargetClassRO: # NOTE: not a dataclass def is_swift(self) -> bool: return self.flags & 0x1 def has_cxx_dtor(self) -> bool: return self.flags & (0x1 << 2)
[docs] @dc.dataclass class TargetClassDataRaw64(TargetClassRO, Virtual): """Struct equivalent to ``class_data_ro_t``.""" #: class flags (most common *is_swift* and *has_cxx_dtor*) flags: uint32_t = csd.csfield(cs.Int32ul) #: The instance starting point (unused) instance_start: uint32_t = csd.csfield(cs.Int32ul) #: The instance end point (unused) instance_end: uint32_t = csd.csfield(cs.Int32ul) #: Additional field on 64-bit systems (unused) reserved: uint32_t = csd.csfield(cs.Int32ul) #: A reference to the IVar layout - always zero (unused) ivar_layout: uintptr_t = csd.csfield(Pointer64) #: Pointer to the source-written name of this class name: uintptr_t = csd.csfield(Pointer64) #: A reference to all defined instance methods base_methods: uintptr_t = csd.csfield(Pointer64) #: A reference to all conformed protocols base_protocols: uintptr_t = csd.csfield(Pointer64) #: A pointer to a list of defined instance variables ivars: uintptr_t = csd.csfield(Pointer64) #: Reference to the weak instance variable layout (unused) weak_ivar_layout: uintptr_t = csd.csfield(Pointer64) #: A pointer to a list of all instance properties base_properties: uintptr_t = csd.csfield(Pointer64)
@dc.dataclass class TargetClassDataRaw32(TargetClassRO, Virtual): flags: uint32_t = csd.csfield(cs.Int32ul) instance_start: uint32_t = csd.csfield(cs.Int32ul) instance_end: uint32_t = csd.csfield(cs.Int32ul) ivar_layout: uintptr_t = csd.csfield(Pointer32) name: uintptr_t = csd.csfield(Pointer32) base_methods: uintptr_t = csd.csfield(Pointer32) base_protocols: uintptr_t = csd.csfield(Pointer32) ivars: uintptr_t = csd.csfield(Pointer32) weak_ivar_layout: uintptr_t = csd.csfield(Pointer32) base_properties: uintptr_t = csd.csfield(Pointer32) class ClassDataBits: def is_swift_stable(self) -> bool: return bool(self.bits & (1 << 1)) def is_swift_legacy(self) -> bool: return bool(self.bits & (1 << 0)) def is_swift(self) -> bool: """Returns whether the referenced class data points to a swift class. :return: whether the class is a swift class :rtype: bool """ return self.is_swift_legacy() or self.is_swift_stable() @dc.dataclass class ClassDataBits32(ClassDataBits): bits: uint32_t = csd.csfield(cs.Int32ul) ptr: uintptr_t = csd.csfield(cs.Computed(lambda ctx: ctx.bits & 0xFFFFFFFC))
[docs] @dc.dataclass class ClassDataBits64(ClassDataBits): """Additional data structure to measure the referenced class data location.""" #: The actual bits of a reference bits: uint64_t = csd.csfield(cs.Int64ul) #: The 'decoded' pointer (computed at runtime) ptr: uintptr_t = csd.csfield(cs.Computed(lambda ctx: ctx.bits & 0x00007FFFFFFFFFF8))
[docs] @dc.dataclass class TargetObjCClassRaw64(TargetObjCObjectRaw64): """The basic structure of Objective-C class interfaces.""" #: The super class of this Objective-C class interface. superclass: uintptr_t = csd.csfield(Pointer64) #: Internal cache pointer (unused) cache: uintptr_t = csd.csfield(Pointer64) #: Reference to the internal VTable of this class interface (unused). vtable: uintptr_t = csd.csfield(Pointer64) #: A mangled reference to the actual class data. data: ClassDataBits64 = csd.csfield(ClassDataBits64)
@dc.dataclass class TargetObjCClassRaw32(TargetObjCObjectRaw32): # metaclass: int = csd.csfield(Pointer32) === isa superclass: uintptr_t = csd.csfield(Pointer32) cache: uintptr_t = csd.csfield(Pointer32) vtable: uintptr_t = csd.csfield(Pointer32) data: ClassDataBits32 = csd.csfield(ClassDataBits32) @dc.dataclass class TargetSwiftClassRaw64(TargetObjCClassRaw64): flags: uint32_t = csd.csfield(cs.Int32ul) instance_address_offset: uint32_t = csd.csfield(cs.Int32ul) instance_size: uint32_t = csd.csfield(cs.Int32ul) instance_align_mask: uint16_t = csd.csfield(cs.Int16ul) reserved: uint16_t = csd.csfield(cs.Int16ul) class_size: uint32_t = csd.csfield(cs.Int32ul) class_address_offset: uint32_t = csd.csfield(cs.Int32ul) description: uintptr_t = csd.csfield(Pointer64) def base_address(self) -> int: return self._address - self.class_address_offset @dc.dataclass class TargetSwiftClassRaw32(TargetObjCClassRaw32): flags: uint32_t = csd.csfield(cs.Int32ul) instance_address_offset: uint32_t = csd.csfield(cs.Int32ul) instance_size: uint32_t = csd.csfield(cs.Int32ul) instance_align_mask: uint16_t = csd.csfield(cs.Int16ul) reserved: uint16_t = csd.csfield(cs.Int16ul) class_size: uint32_t = csd.csfield(cs.Int32ul) class_address_offset: uint32_t = csd.csfield(cs.Int32ul) description: uintptr_t = csd.csfield(Pointer32) def base_address(self) -> int: return self._address - self.class_address_offset
[docs] @dc.dataclass class TargetCategoryRaw64(Virtual): """Raw struct of an Objective-C Category""" #: the catgory's name name: uintptr_t = csd.csfield(Pointer64) #: a reference to the base class of this category base_class: uintptr_t = csd.csfield(Pointer64) #: a reference to a list of instance methods instance_methods: uintptr_t = csd.csfield(Pointer64) #: a reference to all defined additional class methods class_methods: uintptr_t = csd.csfield(Pointer64) #: a reference to all additional conformed protocols base_protocols: uintptr_t = csd.csfield(Pointer64) #: pointer to a property list structure instance_properties: uintptr_t = csd.csfield(Pointer64)
@dc.dataclass class TargetCategoryRaw32(Virtual): name: uintptr_t = csd.csfield(Pointer32) base_class: uintptr_t = csd.csfield(Pointer32) instance_methods: uintptr_t = csd.csfield(Pointer32) class_methods: uintptr_t = csd.csfield(Pointer32) base_protocols: uintptr_t = csd.csfield(Pointer32) instance_properties: uintptr_t = csd.csfield(Pointer32) # There is no documentation on 32-bit CFStrings, so we won't # include that structure @dc.dataclass class TargetCFStringRaw64(TargetObjCObjectRaw64): flags: uint64_t = csd.csfield(cs.Int64ul) value: uintptr_t = csd.csfield(Pointer64) characters: uint64_t = csd.csfield(cs.Int64ul) # Typing extras RawClassT = t.Union[TargetObjCClassRaw32, TargetObjCClassRaw64] RawSwiftClassT = t.Union[TargetSwiftClassRaw64, TargetSwiftClassRaw32] RawClassDataT = t.Union[TargetClassDataRaw32, TargetClassDataRaw64] RawProtocolT = t.Union[TargetProtocolRaw32, TargetProtocolRaw64] RawCategoryT = t.Union[TargetCategoryRaw32, TargetCategoryRaw64] RawIVarT = t.Union[TargetIVarRaw32, TargetIVarRaw64] RawPropertyT = t.Union[TargetPropertyRaw32, TargetPropertyRaw64] RawMethodT = t.Union[TargetMethodRaw32, TargetMethodRaw64]