diff --git a/Sources/MachOKit/Model/DyldCache/Loader/ObjCBinaryInfo.swift b/Sources/MachOKit/Model/DyldCache/Loader/ObjCBinaryInfo.swift new file mode 100644 index 0000000..73b04c6 --- /dev/null +++ b/Sources/MachOKit/Model/DyldCache/Loader/ObjCBinaryInfo.swift @@ -0,0 +1,15 @@ +// +// ObjCBinaryInfo.swift +// MachOKit +// +// Created by p-x9 on 2024/11/16 +// +// + +import Foundation + +public struct ObjCBinaryInfo: LayoutWrapper { + public typealias Layout = objc_binary_info + + public var layout: Layout +} diff --git a/Sources/MachOKit/Model/DyldCache/Loader/PrebuiltLoader.swift b/Sources/MachOKit/Model/DyldCache/Loader/PrebuiltLoader.swift index 3f0742a..6228d09 100644 --- a/Sources/MachOKit/Model/DyldCache/Loader/PrebuiltLoader.swift +++ b/Sources/MachOKit/Model/DyldCache/Loader/PrebuiltLoader.swift @@ -21,9 +21,25 @@ extension PrebuiltLoader { layout.loader.isPrebuilt != 0 } + public var neverUnload: Bool { + layout.loader.neverUnload != 0 + } + + public var isPremapped: Bool { + layout.loader.isPremapped != 0 + } + public var ref: LoaderRef { .init(layout: layout.loader.ref) } + + public var uuid: UUID { + .init(uuid: layout.loader.uuid) + } + + public var sectionLocations: SectionLocations { + .init(layout: layout.sectionLocations) + } } extension PrebuiltLoader { @@ -34,6 +50,14 @@ extension PrebuiltLoader { return cache.fileHandle.readString(offset: offset) } + public func altPath(in cache: DyldCache) -> String? { + guard layout.altPathOffset != 0 else { return nil } + guard let offset = cache.fileOffset( + of: numericCast(address) + numericCast(layout.altPathOffset) + ) else { return nil } + return cache.fileHandle.readString(offset: offset) + } + public func dependentLoaderRefs(in cache: DyldCache) -> DataSequence? { guard layout.dependentLoaderRefsArrayOffset != 0, let offset = cache.fileOffset( @@ -46,6 +70,14 @@ extension PrebuiltLoader { numberOfElements: numericCast(layout.depCount) ) } + + public func objcBinaryInfo(in cache: DyldCache) -> ObjCBinaryInfo? { + guard layout.objcBinaryInfoOffset != 0 else { return nil } + guard let offset = cache.fileOffset( + of: numericCast(address) + numericCast(layout.objcBinaryInfoOffset) + ) else { return nil } + return cache.fileHandle.read(offset: offset) + } } extension PrebuiltLoader { @@ -61,6 +93,19 @@ extension PrebuiltLoader { ) } + public func altPath(in cache: DyldCacheLoaded) -> String? { + // swiftlint:disable:previous unused_parameter + guard layout.altPathOffset != 0 else { return nil } + guard let baseAddress = UnsafeRawPointer(bitPattern: address) else { + return nil + } + return String( + cString: baseAddress + .advanced(by: numericCast(layout.altPathOffset)) + .assumingMemoryBound(to: CChar.self) + ) + } + public func dependentLoaderRefs(in cache: DyldCacheLoaded) -> MemorySequence? { // swiftlint:disable:previous unused_parameter guard layout.dependentLoaderRefsArrayOffset != 0, @@ -74,6 +119,18 @@ extension PrebuiltLoader { numberOfElements: numericCast(layout.depCount) ) } + + public func objcBinaryInfo(in cache: DyldCacheLoaded) -> ObjCBinaryInfo? { + // swiftlint:disable:previous unused_parameter + guard layout.objcBinaryInfoOffset != 0, + let baseAddress = UnsafeRawPointer(bitPattern: address) else { + return nil + } + return baseAddress + .advanced(by: numericCast(layout.objcBinaryInfoOffset)) + .assumingMemoryBound(to: ObjCBinaryInfo.self) + .pointee + } } extension PrebuiltLoader { diff --git a/Sources/MachOKit/Model/DyldCache/Loader/PrebuiltLoaderProtocol.swift b/Sources/MachOKit/Model/DyldCache/Loader/PrebuiltLoaderProtocol.swift index 0f736d9..aaef887 100644 --- a/Sources/MachOKit/Model/DyldCache/Loader/PrebuiltLoaderProtocol.swift +++ b/Sources/MachOKit/Model/DyldCache/Loader/PrebuiltLoaderProtocol.swift @@ -17,25 +17,47 @@ public protocol PrebuiltLoaderProtocol { /// magic of loader starts var magic: String? { get } - /// PrebuiltLoader vs JustInTimeLoader + /// PrebuiltLoader vs JustInTimeLoader var isPrebuilt: Bool { get } + var neverUnload: Bool { get } + var isPremapped: Bool { get } + var ref: LoaderRef { get } + // Information for all pre-calculated sections that we know about + var sectionLocations: SectionLocations { get } + /// path for target mach-o image /// - Parameter cache: DyldCache to which `self` belongs /// - Returns: path name func path(in cache: DyldCache) -> String? + /// alternative path for target mach-o image if install_name does not match real path + /// - Parameter cache: DyldCache to which `self` belongs + /// - Returns: path name + func altPath(in cache: DyldCache) -> String? /// loader reference list of target 's dependencies /// - Parameter cache: DyldCache to which `self` belongs /// - Returns: sequence of loader reference func dependentLoaderRefs(in cache: DyldCache) -> DataSequence? + /// Stores information about the layout of the objc sections in a binary + /// - Parameter cache: DyldCache to which `self` belongs + /// - Returns: binary info for objc + func objcBinaryInfo(in cache: DyldCache) -> ObjCBinaryInfo? /// path for target mach-o image /// - Parameter cache: DyldCacheLoaded to which `self` belongs /// - Returns: path name func path(in cache: DyldCacheLoaded) -> String? + /// alternative path for target mach-o image if install_name does not match real path + /// - Parameter cache: DyldCacheLoaded to which `self` belongs + /// - Returns: path name + func altPath(in cache: DyldCacheLoaded) -> String? /// loader reference list of target 's dependencies /// - Parameter cache: DyldCacheLoaded to which `self` belongs /// - Returns: sequence of loader reference func dependentLoaderRefs(in cache: DyldCacheLoaded) -> MemorySequence? + /// Stores information about the layout of the objc sections in a binary + /// - Parameter cache: DyldCacheLoaded to which `self` belongs + /// - Returns: binary info for objc + func objcBinaryInfo(in cache: DyldCacheLoaded) -> ObjCBinaryInfo? } diff --git a/Sources/MachOKit/Model/DyldCache/Loader/PrebuiltLoader_Pre1165_3.swift b/Sources/MachOKit/Model/DyldCache/Loader/PrebuiltLoader_Pre1165_3.swift index 3fb6a8e..be2b40f 100644 --- a/Sources/MachOKit/Model/DyldCache/Loader/PrebuiltLoader_Pre1165_3.swift +++ b/Sources/MachOKit/Model/DyldCache/Loader/PrebuiltLoader_Pre1165_3.swift @@ -19,9 +19,21 @@ extension PrebuiltLoader_Pre1165_3 { layout.loader.isPrebuilt != 0 } + public var neverUnload: Bool { + layout.loader.neverUnload != 0 + } + + public var isPremapped: Bool { + layout.loader.isPremapped != 0 + } + public var ref: LoaderRef { .init(layout: layout.loader.ref) } + + public var sectionLocations: SectionLocations { + .init(layout: layout.sectionLocations) + } } extension PrebuiltLoader_Pre1165_3 { @@ -32,6 +44,14 @@ extension PrebuiltLoader_Pre1165_3 { return cache.fileHandle.readString(offset: offset) } + public func altPath(in cache: DyldCache) -> String? { + guard layout.altPathOffset != 0 else { return nil } + guard let offset = cache.fileOffset( + of: numericCast(address) + numericCast(layout.altPathOffset) + ) else { return nil } + return cache.fileHandle.readString(offset: offset) + } + public func dependentLoaderRefs(in cache: DyldCache) -> DataSequence? { guard layout.dependentLoaderRefsArrayOffset != 0, let offset = cache.fileOffset( @@ -44,6 +64,14 @@ extension PrebuiltLoader_Pre1165_3 { numberOfElements: numericCast(layout.depCount) ) } + + public func objcBinaryInfo(in cache: DyldCache) -> ObjCBinaryInfo? { + guard layout.objcBinaryInfoOffset != 0 else { return nil } + guard let offset = cache.fileOffset( + of: numericCast(address) + numericCast(layout.objcBinaryInfoOffset) + ) else { return nil } + return cache.fileHandle.read(offset: offset) + } } extension PrebuiltLoader_Pre1165_3 { @@ -59,6 +87,19 @@ extension PrebuiltLoader_Pre1165_3 { ) } + public func altPath(in cache: DyldCacheLoaded) -> String? { + // swiftlint:disable:previous unused_parameter + guard layout.altPathOffset != 0 else { return nil } + guard let baseAddress = UnsafeRawPointer(bitPattern: address) else { + return nil + } + return String( + cString: baseAddress + .advanced(by: numericCast(layout.altPathOffset)) + .assumingMemoryBound(to: CChar.self) + ) + } + public func dependentLoaderRefs(in cache: DyldCacheLoaded) -> MemorySequence? { // swiftlint:disable:previous unused_parameter guard layout.dependentLoaderRefsArrayOffset != 0, @@ -72,6 +113,18 @@ extension PrebuiltLoader_Pre1165_3 { numberOfElements: numericCast(layout.depCount) ) } + + public func objcBinaryInfo(in cache: DyldCacheLoaded) -> ObjCBinaryInfo? { + // swiftlint:disable:previous unused_parameter + guard layout.objcBinaryInfoOffset != 0, + let baseAddress = UnsafeRawPointer(bitPattern: address) else { + return nil + } + return baseAddress + .advanced(by: numericCast(layout.objcBinaryInfoOffset)) + .assumingMemoryBound(to: ObjCBinaryInfo.self) + .pointee + } } extension PrebuiltLoader_Pre1165_3 { diff --git a/Sources/MachOKit/Model/DyldCache/Loader/SectionLocations.swift b/Sources/MachOKit/Model/DyldCache/Loader/SectionLocations.swift new file mode 100644 index 0000000..de924b7 --- /dev/null +++ b/Sources/MachOKit/Model/DyldCache/Loader/SectionLocations.swift @@ -0,0 +1,77 @@ +// +// SectionLocations.swift +// MachOKit +// +// Created by p-x9 on 2024/11/16 +// +// + +import Foundation + +public struct SectionLocations: LayoutWrapper { + public typealias Layout = section_locations + + public var layout: Layout +} + +extension SectionLocations { + // [dyld implementation](https://github.com/apple-oss-distributions/dyld/blob/65bbeed63cec73f313b1d636e63f243964725a9d/include/mach-o/dyld_priv.h#L62) + public enum SectionKind: Int, CaseIterable { + // TEXT: + case text_swift5_protos + case text_swift5_proto + case text_swift5_types + case text_swift5_replace + case text_swift5_replace2 + case text_swift5_ac_funcs + + // DATA*: + case objc_image_info + case data_sel_refs + case data_msg_refs + case data_class_refs + case data_super_refs + case data_protocol_refs + case data_class_list + case data_non_lazy_class_list + case data_stub_list + case data_category_list + case data_category_list2 + case data_non_lazy_category_list + case data_protocol_list + case data_objc_fork_ok + case data_raw_isa + + // ~~ version 1 ~~ + } +} + +extension SectionLocations { + public struct Section { + public let offset: Int + public let size: Int + public let kind: SectionKind + } +} + +extension SectionLocations { + public func section(for kind: SectionKind) -> Section { + var offsets = layout.offsets + var sizes = layout.sizes + let offset = withUnsafePointer(to: &offsets) { + UnsafeRawPointer($0) + .assumingMemoryBound(to: UInt64.self) + .advanced(by: kind.rawValue).pointee + } + let size = withUnsafePointer(to: &sizes) { + UnsafeRawPointer($0) + .assumingMemoryBound(to: UInt64.self) + .advanced(by: kind.rawValue).pointee + } + return .init( + offset: numericCast(offset), + size: numericCast(size), + kind: kind + ) + } +} diff --git a/Sources/MachOKitC/include/dyld_cache_loader.h b/Sources/MachOKitC/include/dyld_cache_loader.h index 280012f..72dd371 100644 --- a/Sources/MachOKitC/include/dyld_cache_loader.h +++ b/Sources/MachOKitC/include/dyld_cache_loader.h @@ -9,6 +9,7 @@ #ifndef dyld_cache_loader_h #define dyld_cache_loader_h +#include // https://github.com/apple-oss-distributions/dyld/blob/d552c40cd1de105f0ec95008e0e0c0972de43456/dyld/PrebuiltLoader.h#L254 struct prebuilt_loader_set { uint32_t magic; @@ -175,7 +176,55 @@ struct prebuilt_loader { uint32_t overrideBindTargetRefsOffset; uint32_t overrideBindTargetRefsCount; - // struct section_locations sectionLocations; + struct section_locations sectionLocations; +}; + +// https://github.com/apple-oss-distributions/dyld/blob/65bbeed63cec73f313b1d636e63f243964725a9d/dyld/PrebuiltLoader.h#L344 +// Stores information about the layout of the objc sections in a binary, as well as other properties relating to +// the objc information in there. +struct objc_binary_info { + // Offset to the __objc_imageinfo section + uint64_t imageInfoRuntimeOffset; + + // Offsets to sections containing objc pointers + uint64_t selRefsRuntimeOffset; + uint64_t classListRuntimeOffset; + uint64_t categoryListRuntimeOffset; + uint64_t protocolListRuntimeOffset; + + // Counts of the above sections. + uint32_t selRefsCount; + uint32_t classListCount; + uint32_t categoryCount; + uint32_t protocolListCount; + + // Do we have stable Swift fixups to apply to at least one class? + bool hasClassStableSwiftFixups; + + // Do we have any pointer-based method lists to set as uniqued? + bool hasClassMethodListsToSetUniqued; + bool hasCategoryMethodListsToSetUniqued; + bool hasProtocolMethodListsToSetUniqued; + + // Do we have any method lists in which to set selector references. + // Note we only support visiting selector refernces in pointer based method lists + // Relative method lists should have been verified to always point to __objc_selrefs + bool hasClassMethodListsToUnique; + bool hasCategoryMethodListsToUnique; + bool hasProtocolMethodListsToUnique; + + // Whwn serialized to the PrebuildLoader, these fields will encode other information about + // the binary. + + // Offset to an array of uint8_t's. One for each protocol. + // Note this can be 0 (ie, have no fixups), even if we have protocols. That would be the case + // if this binary contains no canonical protocol definitions, ie, all canonical defs are in other binaries + // or the shared cache. + uint32_t protocolFixupsOffset; + // Offset to an array of BindTargetRef's. One for each selector reference to fix up + // Note we only fix up selector refs in the __objc_selrefs section, and in pointer-based method lists + uint32_t selectorReferencesFixupsOffset; + uint32_t selectorReferencesFixupsCount; }; #endif /* dyld_cache_loader_h */