From a01b72562a0deaa62903285b93cdfcfabe9f33a4 Mon Sep 17 00:00:00 2001 From: p-x9 <50244599+p-x9@users.noreply.github.com> Date: Sun, 27 Oct 2024 12:35:30 +0900 Subject: [PATCH 1/4] Refactor methods for obtaining machO from objc header info ro --- .../ObjCOptimization/ObjCHeaderInfoRO.swift | 102 ++---------------- .../ObjCHeaderOptimizationRO.swift | 16 ++- .../DyldCacheLoadedPrintTests.swift | 3 +- Tests/MachOKitTests/DyldCachePrintTests.swift | 1 - 4 files changed, 20 insertions(+), 102 deletions(-) diff --git a/Sources/MachOKit/Model/DyldCache/ObjCOptimization/ObjCHeaderInfoRO.swift b/Sources/MachOKit/Model/DyldCache/ObjCOptimization/ObjCHeaderInfoRO.swift index a598e91..b6c7ea1 100644 --- a/Sources/MachOKit/Model/DyldCache/ObjCOptimization/ObjCHeaderInfoRO.swift +++ b/Sources/MachOKit/Model/DyldCache/ObjCOptimization/ObjCHeaderInfoRO.swift @@ -26,48 +26,20 @@ public protocol ObjCHeaderInfoROProtocol { /// Target mach-o image of header /// - Parameters: - /// - objcOptimization: objc optimization to which `self` belongs /// - roOptimizaion: ro optimization to which `self` belongs /// - cache: DyldCache to which `self` belongs /// - Returns: mach-o file func machO( - objcOptimization: ObjCOptimization, roOptimizaion: HeaderOptimizationRO, in cache: DyldCache ) -> MachOFile? /// Target mach-o image of header /// - Parameters: - /// - objcOptimization: objc optimization to which `self` belongs - /// - roOptimizaion: ro optimization to which `self` belongs - /// - cache: DyldCache to which `self` belongs - /// - Returns: mach-o file - func machO( - objcOptimization: OldObjCOptimization, - roOptimizaion: HeaderOptimizationRO, - in cache: DyldCache - ) -> MachOFile? - - /// Target mach-o image of header - /// - Parameters: - /// - objcOptimization: objc optimization to which `self` belongs - /// - roOptimizaion: ro optimization to which `self` belongs - /// - cache: DyldCacheLoaded to which `self` belongs - /// - Returns: mach-o file - func machO( - objcOptimization: ObjCOptimization, - roOptimizaion: HeaderOptimizationRO, - in cache: DyldCacheLoaded - ) -> MachOImage? - - /// Target mach-o image of header - /// - Parameters: - /// - objcOptimization: objc optimization to which `self` belongs /// - roOptimizaion: ro optimization to which `self` belongs /// - cache: DyldCacheLoaded to which `self` belongs /// - Returns: mach-o file func machO( - objcOptimization: OldObjCOptimization, roOptimizaion: HeaderOptimizationRO, in cache: DyldCacheLoaded ) -> MachOImage? @@ -95,63 +67,34 @@ public struct ObjCHeaderInfoRO64: LayoutWrapper, ObjCHeaderInfoROProtocol { } public func machO( - objcOptimization: ObjCOptimization, - roOptimizaion: HeaderOptimizationRO, - in cache: DyldCache - ) -> MachOFile? { - _machO( - headerInfoROOffset: objcOptimization.headerInfoROCacheOffset, - roOptimizaion: roOptimizaion, - in: cache - ) - } - - public func machO( - objcOptimization: OldObjCOptimization, roOptimizaion: HeaderOptimizationRO, in cache: DyldCache ) -> MachOFile? { _machO( - headerInfoROOffset: numericCast(objcOptimization.offset) + numericCast(objcOptimization.headeropt_ro_offset), roOptimizaion: roOptimizaion, in: cache ) } public func machO( - objcOptimization: ObjCOptimization, roOptimizaion: HeaderOptimizationRO, in cache: DyldCacheLoaded ) -> MachOImage? { _machO( - headerInfoROOffset: objcOptimization.headerInfoROCacheOffset, - roOptimizaion: roOptimizaion, - in: cache - ) - } - - public func machO( - objcOptimization: OldObjCOptimization, - roOptimizaion: HeaderOptimizationRO, - in cache: DyldCacheLoaded - ) -> MachOImage? { - _machO( - headerInfoROOffset: numericCast(objcOptimization.offset) + numericCast(objcOptimization.headeropt_ro_offset), roOptimizaion: roOptimizaion, in: cache ) } private func _machO( - headerInfoROOffset: UInt64, roOptimizaion: HeaderOptimizationRO, in cache: DyldCache ) -> MachOFile? { let offsetFromRoHeader = roOptimizaion.layoutSize + index * roOptimizaion.entrySize let sharedRegionStart = cache.mainCacheHeader.sharedRegionStart - let roOffset = headerInfoROOffset + sharedRegionStart - let _offset: Int = numericCast(roOffset) + offsetFromRoHeader + numericCast(layout.mhdr_offset) + let roOffset = roOptimizaion.offset + numericCast(sharedRegionStart) + let _offset: Int = roOffset + offsetFromRoHeader + numericCast(layout.mhdr_offset) guard let offset = cache.fileOffset( of: numericCast(_offset) ) else { @@ -165,7 +108,6 @@ public struct ObjCHeaderInfoRO64: LayoutWrapper, ObjCHeaderInfoROProtocol { } private func _machO( - headerInfoROOffset: UInt64, roOptimizaion: HeaderOptimizationRO, in cache: DyldCacheLoaded ) -> MachOImage? { @@ -173,8 +115,8 @@ public struct ObjCHeaderInfoRO64: LayoutWrapper, ObjCHeaderInfoROProtocol { let offsetFromRoHeader = roOptimizaion.layoutSize + index * roOptimizaion.entrySize let sharedRegionStart = cache.mainCacheHeader.sharedRegionStart - let roOffset = headerInfoROOffset + sharedRegionStart + numericCast(slide) - let _offset: Int = numericCast(roOffset) + offsetFromRoHeader + numericCast(layout.mhdr_offset) + let roOffset = roOptimizaion.offset + numericCast(sharedRegionStart) + numericCast(slide) + let _offset: Int = roOffset + offsetFromRoHeader + numericCast(layout.mhdr_offset) guard let ptr = UnsafeRawPointer(bitPattern: _offset) else { return nil } @@ -207,62 +149,33 @@ public struct ObjCHeaderInfoRO32: LayoutWrapper, ObjCHeaderInfoROProtocol { } public func machO( - objcOptimization: ObjCOptimization, roOptimizaion: HeaderOptimizationRO, in cache: DyldCache ) -> MachOFile? { _machO( - headerInfoROOffset: objcOptimization.headerInfoROCacheOffset, roOptimizaion: roOptimizaion, in: cache ) } public func machO( - objcOptimization: OldObjCOptimization, - roOptimizaion: HeaderOptimizationRO, - in cache: DyldCache - ) -> MachOFile? { - _machO( - headerInfoROOffset: numericCast(objcOptimization.offset) + numericCast(objcOptimization.headeropt_ro_offset), - roOptimizaion: roOptimizaion, - in: cache - ) - } - - public func machO( - objcOptimization: ObjCOptimization, roOptimizaion: HeaderOptimizationRO, in cache: DyldCacheLoaded ) -> MachOImage? { _machO( - headerInfoROOffset: objcOptimization.headerInfoROCacheOffset, - roOptimizaion: roOptimizaion, - in: cache - ) - } - - public func machO( - objcOptimization: OldObjCOptimization, - roOptimizaion: HeaderOptimizationRO, - in cache: DyldCacheLoaded - ) -> MachOImage? { - _machO( - headerInfoROOffset: numericCast(objcOptimization.offset) + numericCast(objcOptimization.headeropt_ro_offset), roOptimizaion: roOptimizaion, in: cache ) } private func _machO( - headerInfoROOffset: UInt64, roOptimizaion: HeaderOptimizationRO, in cache: DyldCache ) -> MachOFile? { let offsetFromRoHeader = roOptimizaion.layoutSize + index * numericCast(roOptimizaion.entsize) let sharedRegionStart = cache.mainCacheHeader.sharedRegionStart - let roOffset = headerInfoROOffset + sharedRegionStart + let roOffset = roOptimizaion.offset + numericCast(sharedRegionStart) let _offset: Int = numericCast(roOffset) + offsetFromRoHeader + numericCast(layout.mhdr_offset) guard let offset = cache.fileOffset( of: numericCast(_offset) @@ -277,7 +190,6 @@ public struct ObjCHeaderInfoRO32: LayoutWrapper, ObjCHeaderInfoROProtocol { } private func _machO( - headerInfoROOffset: UInt64, roOptimizaion: HeaderOptimizationRO, in cache: DyldCacheLoaded ) -> MachOImage? { @@ -285,8 +197,8 @@ public struct ObjCHeaderInfoRO32: LayoutWrapper, ObjCHeaderInfoROProtocol { let offsetFromRoHeader = roOptimizaion.layoutSize + index * numericCast(roOptimizaion.entsize) let sharedRegionStart = cache.mainCacheHeader.sharedRegionStart - let roOffset = headerInfoROOffset + sharedRegionStart + numericCast(slide) - let _offset: Int = numericCast(roOffset) + offsetFromRoHeader + numericCast(layout.mhdr_offset) + let roOffset = roOptimizaion.offset + numericCast(sharedRegionStart) + numericCast(slide) + let _offset: Int = roOffset + offsetFromRoHeader + numericCast(layout.mhdr_offset) guard let ptr = UnsafeRawPointer(bitPattern: _offset) else { return nil } diff --git a/Sources/MachOKit/Model/DyldCache/ObjCOptimization/ObjCHeaderOptimizationRO.swift b/Sources/MachOKit/Model/DyldCache/ObjCOptimization/ObjCHeaderOptimizationRO.swift index e771aa5..f343daa 100644 --- a/Sources/MachOKit/Model/DyldCache/ObjCOptimization/ObjCHeaderOptimizationRO.swift +++ b/Sources/MachOKit/Model/DyldCache/ObjCOptimization/ObjCHeaderOptimizationRO.swift @@ -34,7 +34,9 @@ public struct ObjCHeaderOptimizationRO64: LayoutWrapper, ObjCHeaderOptimizationR public var count: Int { numericCast(layout.count) } public var entrySize: Int { numericCast(layout.entsize) } - public func headerInfos(in cache: DyldCache) -> AnyRandomAccessCollection { + public func headerInfos( + in cache: DyldCache + ) -> AnyRandomAccessCollection { precondition( layout.entsize >= HeaderInfo.layoutSize, "entsize is smaller than HeaderInfo" @@ -56,7 +58,9 @@ public struct ObjCHeaderOptimizationRO64: LayoutWrapper, ObjCHeaderOptimizationR ) } - public func headerInfos(in cache: DyldCacheLoaded) -> AnyRandomAccessCollection { + public func headerInfos( + in cache: DyldCacheLoaded + ) -> AnyRandomAccessCollection { precondition( layout.entsize >= HeaderInfo.layoutSize, "entsize is smaller than HeaderInfo" @@ -92,7 +96,9 @@ public struct ObjCHeaderOptimizationRO32: LayoutWrapper, ObjCHeaderOptimizationR public var count: Int { numericCast(layout.count) } public var entrySize: Int { numericCast(layout.entsize) } - public func headerInfos(in cache: DyldCache) -> AnyRandomAccessCollection { + public func headerInfos( + in cache: DyldCache + ) -> AnyRandomAccessCollection { precondition( layout.entsize >= HeaderInfo.layoutSize, "entsize is smaller than HeaderInfo" @@ -114,7 +120,9 @@ public struct ObjCHeaderOptimizationRO32: LayoutWrapper, ObjCHeaderOptimizationR ) } - public func headerInfos(in cache: DyldCacheLoaded) -> AnyRandomAccessCollection { + public func headerInfos( + in cache: DyldCacheLoaded) + -> AnyRandomAccessCollection { precondition( layout.entsize >= HeaderInfo.layoutSize, "entsize is smaller than HeaderInfo" diff --git a/Tests/MachOKitTests/DyldCacheLoadedPrintTests.swift b/Tests/MachOKitTests/DyldCacheLoadedPrintTests.swift index 809b242..3702ee0 100644 --- a/Tests/MachOKitTests/DyldCacheLoadedPrintTests.swift +++ b/Tests/MachOKitTests/DyldCacheLoadedPrintTests.swift @@ -256,7 +256,7 @@ final class DyldCacheLoadedPrintTests: XCTestCase { } func testObjCHeaderOptimizationRO() throws { - guard let objcOptimization = cache.objcOptimization else { return } + guard let objcOptimization = cache.oldObjcOptimization else { return } let ro = objcOptimization.headerOptimizationRO64(in: cache)! let roHeaders = ro.headerInfos(in: cache) print("Count:", ro.count) @@ -274,7 +274,6 @@ final class DyldCacheLoadedPrintTests: XCTestCase { print("Image:") for info in roHeaders { guard let machO = info.machO( - objcOptimization: objcOptimization, roOptimizaion: ro, in: cache ) else { diff --git a/Tests/MachOKitTests/DyldCachePrintTests.swift b/Tests/MachOKitTests/DyldCachePrintTests.swift index 4611c0f..31d238b 100644 --- a/Tests/MachOKitTests/DyldCachePrintTests.swift +++ b/Tests/MachOKitTests/DyldCachePrintTests.swift @@ -259,7 +259,6 @@ final class DyldCachePrintTests: XCTestCase { print("Image:") for info in roHeaders { guard let machO = info.machO( - objcOptimization: objcOptimization, roOptimizaion: ro, in: cache ) else { From 7b1396b04663fa86221e047e0ac80682577fbe04 Mon Sep 17 00:00:00 2001 From: p-x9 <50244599+p-x9@users.noreply.github.com> Date: Sun, 27 Oct 2024 13:34:06 +0900 Subject: [PATCH 2/4] Fix to use actual entrysize instead of `ObjCHeaderInfoRO.layoutSize` --- .../ObjCOptimization/ObjCHeaderOptimizationRO.swift | 12 +++++++----- .../ObjCOptimization/ObjCHeaderOptimizationRW.swift | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Sources/MachOKit/Model/DyldCache/ObjCOptimization/ObjCHeaderOptimizationRO.swift b/Sources/MachOKit/Model/DyldCache/ObjCOptimization/ObjCHeaderOptimizationRO.swift index f343daa..4ae5367 100644 --- a/Sources/MachOKit/Model/DyldCache/ObjCOptimization/ObjCHeaderOptimizationRO.swift +++ b/Sources/MachOKit/Model/DyldCache/ObjCOptimization/ObjCHeaderOptimizationRO.swift @@ -9,7 +9,8 @@ import Foundation public protocol ObjCHeaderOptimizationROProtocol { - associatedtype HeaderInfo: LayoutWrapper + associatedtype HeaderInfo: ObjCHeaderInfoROProtocol + var offset: Int { get } /// number of header infos var count: Int { get } /// layout size of header info @@ -42,6 +43,7 @@ public struct ObjCHeaderOptimizationRO64: LayoutWrapper, ObjCHeaderOptimizationR "entsize is smaller than HeaderInfo" ) let offset = offset + layoutSize + // Warning: HeaderInfo.layoutSize and entrySize are different. return AnyRandomAccessCollection( cache.fileHandle.readDataSequence( offset: numericCast(offset), @@ -51,7 +53,7 @@ public struct ObjCHeaderOptimizationRO64: LayoutWrapper, ObjCHeaderOptimizationR ).enumerated().map({ HeaderInfo( layout: $1, - offset: offset + HeaderInfo.layoutSize * $0, + offset: offset + entrySize * $0, index: $0 ) }) @@ -78,7 +80,7 @@ public struct ObjCHeaderOptimizationRO64: LayoutWrapper, ObjCHeaderOptimizationR .map { HeaderInfo( layout: $1, - offset: offset + HeaderInfo.layoutSize * $0, + offset: offset + entrySize * $0, index: $0 ) } @@ -113,7 +115,7 @@ public struct ObjCHeaderOptimizationRO32: LayoutWrapper, ObjCHeaderOptimizationR ).enumerated().map({ HeaderInfo( layout: $1, - offset: offset + HeaderInfo.layoutSize * $0, + offset: offset + entrySize * $0, index: $0 ) }) @@ -140,7 +142,7 @@ public struct ObjCHeaderOptimizationRO32: LayoutWrapper, ObjCHeaderOptimizationR .map { HeaderInfo( layout: $1, - offset: offset + HeaderInfo.layoutSize * $0, + offset: offset + entrySize * $0, index: $0 ) } diff --git a/Sources/MachOKit/Model/DyldCache/ObjCOptimization/ObjCHeaderOptimizationRW.swift b/Sources/MachOKit/Model/DyldCache/ObjCOptimization/ObjCHeaderOptimizationRW.swift index d02dca5..2616cca 100644 --- a/Sources/MachOKit/Model/DyldCache/ObjCOptimization/ObjCHeaderOptimizationRW.swift +++ b/Sources/MachOKit/Model/DyldCache/ObjCOptimization/ObjCHeaderOptimizationRW.swift @@ -9,7 +9,7 @@ import Foundation public protocol ObjCHeaderOptimizationRWProtocol { - associatedtype HeaderInfo: LayoutWrapper + associatedtype HeaderInfo: ObjCHeaderInfoRWProtocol /// number of header infos var count: Int { get } /// layout size of header info From f570d9f8144d4831b0657c438fb70c166dbe160d Mon Sep 17 00:00:00 2001 From: p-x9 <50244599+p-x9@users.noreply.github.com> Date: Sun, 27 Oct 2024 13:35:10 +0900 Subject: [PATCH 3/4] Refactor calculation of offset for machO --- .../ObjCOptimization/ObjCHeaderInfoRO.swift | 56 ++++++++----------- 1 file changed, 22 insertions(+), 34 deletions(-) diff --git a/Sources/MachOKit/Model/DyldCache/ObjCOptimization/ObjCHeaderInfoRO.swift b/Sources/MachOKit/Model/DyldCache/ObjCOptimization/ObjCHeaderInfoRO.swift index b6c7ea1..fe7aab3 100644 --- a/Sources/MachOKit/Model/DyldCache/ObjCOptimization/ObjCHeaderInfoRO.swift +++ b/Sources/MachOKit/Model/DyldCache/ObjCOptimization/ObjCHeaderInfoRO.swift @@ -15,6 +15,9 @@ public protocol ObjCHeaderInfoROProtocol { /// index of this header info var index: Int { get } + /// offset to mach-o header from start of self + var machOHeaderOffset: Int { get } + /// Description of an Objective-C image /// - Parameter cache: DyldCache to which `self` belongs /// - Returns: image info @@ -54,6 +57,10 @@ public struct ObjCHeaderInfoRO64: LayoutWrapper, ObjCHeaderInfoROProtocol { public let offset: Int public let index: Int + public var machOHeaderOffset: Int { + numericCast(layout.mhdr_offset) + } + public func imageInfo(in cache: DyldCache) -> ObjCImageInfo? { let offset = offset + layoutOffset(of: \.info_offset) + numericCast(layout.info_offset) return cache.fileHandle.read(offset: numericCast(offset)) @@ -90,14 +97,9 @@ public struct ObjCHeaderInfoRO64: LayoutWrapper, ObjCHeaderInfoROProtocol { roOptimizaion: HeaderOptimizationRO, in cache: DyldCache ) -> MachOFile? { - let offsetFromRoHeader = roOptimizaion.layoutSize + index * roOptimizaion.entrySize - - let sharedRegionStart = cache.mainCacheHeader.sharedRegionStart - let roOffset = roOptimizaion.offset + numericCast(sharedRegionStart) - let _offset: Int = roOffset + offsetFromRoHeader + numericCast(layout.mhdr_offset) - guard let offset = cache.fileOffset( - of: numericCast(_offset) - ) else { + let offset = offset + machOHeaderOffset + // Check if the cache file contains offset + guard cache.address(of: numericCast(offset)) != nil else { return nil } return try? .init( @@ -111,15 +113,9 @@ public struct ObjCHeaderInfoRO64: LayoutWrapper, ObjCHeaderInfoROProtocol { roOptimizaion: HeaderOptimizationRO, in cache: DyldCacheLoaded ) -> MachOImage? { - guard let slide = cache.slide else { return nil } - let offsetFromRoHeader = roOptimizaion.layoutSize + index * roOptimizaion.entrySize - - let sharedRegionStart = cache.mainCacheHeader.sharedRegionStart - let roOffset = roOptimizaion.offset + numericCast(sharedRegionStart) + numericCast(slide) - let _offset: Int = roOffset + offsetFromRoHeader + numericCast(layout.mhdr_offset) - guard let ptr = UnsafeRawPointer(bitPattern: _offset) else { - return nil - } + let ptr = cache.ptr + .advanced(by: offset) + .advanced(by: numericCast(layout.mhdr_offset)) return .init( ptr: ptr .assumingMemoryBound(to: mach_header.self) @@ -136,6 +132,10 @@ public struct ObjCHeaderInfoRO32: LayoutWrapper, ObjCHeaderInfoROProtocol { public let offset: Int public let index: Int + public var machOHeaderOffset: Int { + numericCast(layout.mhdr_offset) + } + public func imageInfo(in cache: DyldCache) -> ObjCImageInfo? { let offset = offset + layoutOffset(of: \.info_offset) + numericCast(layout.info_offset) return cache.fileHandle.read(offset: numericCast(offset)) @@ -172,14 +172,8 @@ public struct ObjCHeaderInfoRO32: LayoutWrapper, ObjCHeaderInfoROProtocol { roOptimizaion: HeaderOptimizationRO, in cache: DyldCache ) -> MachOFile? { - let offsetFromRoHeader = roOptimizaion.layoutSize + index * numericCast(roOptimizaion.entsize) - - let sharedRegionStart = cache.mainCacheHeader.sharedRegionStart - let roOffset = roOptimizaion.offset + numericCast(sharedRegionStart) - let _offset: Int = numericCast(roOffset) + offsetFromRoHeader + numericCast(layout.mhdr_offset) - guard let offset = cache.fileOffset( - of: numericCast(_offset) - ) else { + let offset = offset + machOHeaderOffset + guard cache.address(of: numericCast(offset)) != nil else { return nil } return try? .init( @@ -193,15 +187,9 @@ public struct ObjCHeaderInfoRO32: LayoutWrapper, ObjCHeaderInfoROProtocol { roOptimizaion: HeaderOptimizationRO, in cache: DyldCacheLoaded ) -> MachOImage? { - guard let slide = cache.slide else { return nil } - let offsetFromRoHeader = roOptimizaion.layoutSize + index * numericCast(roOptimizaion.entsize) - - let sharedRegionStart = cache.mainCacheHeader.sharedRegionStart - let roOffset = roOptimizaion.offset + numericCast(sharedRegionStart) + numericCast(slide) - let _offset: Int = roOffset + offsetFromRoHeader + numericCast(layout.mhdr_offset) - guard let ptr = UnsafeRawPointer(bitPattern: _offset) else { - return nil - } + let ptr = cache.ptr + .advanced(by: offset) + .advanced(by: numericCast(layout.mhdr_offset)) return .init( ptr: ptr .assumingMemoryBound(to: mach_header.self) From 622b45849244ca8e4429165c30e79da32228036d Mon Sep 17 00:00:00 2001 From: p-x9 <50244599+p-x9@users.noreply.github.com> Date: Sun, 27 Oct 2024 13:52:28 +0900 Subject: [PATCH 4/4] Add method to get objc ro optimization info of specified mach-o --- .../ObjCHeaderOptimizationRO.swift | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/Sources/MachOKit/Model/DyldCache/ObjCOptimization/ObjCHeaderOptimizationRO.swift b/Sources/MachOKit/Model/DyldCache/ObjCOptimization/ObjCHeaderOptimizationRO.swift index 4ae5367..4d06631 100644 --- a/Sources/MachOKit/Model/DyldCache/ObjCOptimization/ObjCHeaderOptimizationRO.swift +++ b/Sources/MachOKit/Model/DyldCache/ObjCOptimization/ObjCHeaderOptimizationRO.swift @@ -149,3 +149,44 @@ public struct ObjCHeaderOptimizationRO32: LayoutWrapper, ObjCHeaderOptimizationR ) } } + +extension ObjCHeaderOptimizationROProtocol where Self: LayoutWrapper { + /// Optimisation info of the specified machO + /// - Parameters: + /// - cache: DyldCache to which `self` belongs + /// - machO: target machO file + /// - Returns: objc ro optimization info for specified machO + public func headerInfo( + in cache: DyldCache, for machO: MachOFile + ) -> HeaderInfo? { + guard machO.headerStartOffsetInCache > 0 else { + return nil + } + return headerInfos(in: cache) + .first( + where: { + let offset = $0.offset + $0.machOHeaderOffset + return machO.headerStartOffsetInCache == offset + } + ) + } + + /// Optimisation info of the specified machO + /// - Parameters: + /// - cache: DyldCacheLoaded to which `self` belongs + /// - machO: target machO image + /// - Returns: objc ro optimization info for specified machO + public func headerInfo( + in cache: DyldCacheLoaded, for machO: MachOImage + ) -> HeaderInfo? { + headerInfos(in: cache) + .first( + where: { + let ptr = cache.ptr + .advanced(by: $0.offset) + .advanced(by: $0.machOHeaderOffset) + return ptr == machO.ptr + } + ) + } +}