diff --git a/Package.resolved b/Package.resolved index 5e84ff2..fcc75c7 100644 --- a/Package.resolved +++ b/Package.resolved @@ -5,8 +5,17 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/p-x9/MachOKit.git", "state" : { - "revision" : "62d22e1ecef3dda702c039f03dd83f674ad59bfc", - "version" : "0.30.0" + "revision" : "753461938fafdcdbcd19b68b34b137dc37367d22", + "version" : "0.34.0" + } + }, + { + "identity" : "swift-fileio", + "kind" : "remoteSourceControl", + "location" : "https://github.com/p-x9/swift-fileio.git", + "state" : { + "revision" : "23349fe1eb23c6ca2876d461a46ff60c0fa92f9c", + "version" : "0.9.0" } }, { diff --git a/Package.swift b/Package.swift index e35d660..a48f75b 100644 --- a/Package.swift +++ b/Package.swift @@ -17,7 +17,7 @@ let package = Package( ) ], dependencies: [ - .package(url: "https://github.com/p-x9/MachOKit.git", from: "0.30.0"), + .package(url: "https://github.com/p-x9/MachOKit.git", from: "0.34.0"), .package(url: "https://github.com/p-x9/swift-objc-dump.git", from: "0.7.0") ], targets: [ diff --git a/Sources/MachOObjCSection/Extension/DyldCache+.swift b/Sources/MachOObjCSection/Extension/DyldCache+.swift index d2d71b2..8acf796 100644 --- a/Sources/MachOObjCSection/Extension/DyldCache+.swift +++ b/Sources/MachOObjCSection/Extension/DyldCache+.swift @@ -115,3 +115,14 @@ extension DyldCache { return nil } } + +extension DyldCache { + func machO(containing unslidAddress: UInt64) -> MachOFile? { + for machO in self.machOFiles() { + if machO.contains(unslidAddress: unslidAddress) { + return machO + } + } + return nil + } +} diff --git a/Sources/MachOObjCSection/Extension/DyldCacheLoaded+.swift b/Sources/MachOObjCSection/Extension/DyldCacheLoaded+.swift index b091152..523e099 100644 --- a/Sources/MachOObjCSection/Extension/DyldCacheLoaded+.swift +++ b/Sources/MachOObjCSection/Extension/DyldCacheLoaded+.swift @@ -99,3 +99,23 @@ extension DyldCacheLoaded { return nil } } + +extension DyldCacheLoaded { + func machO(containing ptr: UnsafeRawPointer) -> MachOImage? { + for machO in machOImages() { + if machO.contains(ptr: ptr) { + return machO + } + } + return nil + } + + func machO(containing unslidAddress: UInt64) -> MachOImage? { + for machO in self.machOImages() { + if machO.contains(unslidAddress: unslidAddress) { + return machO + } + } + return nil + } +} diff --git a/Sources/MachOObjCSection/Model/Protocol/ObjCProtocolList32.swift b/Sources/MachOObjCSection/Model/Protocol/ObjCProtocolList32.swift index 0c38f82..c09a890 100644 --- a/Sources/MachOObjCSection/Model/Protocol/ObjCProtocolList32.swift +++ b/Sources/MachOObjCSection/Model/Protocol/ObjCProtocolList32.swift @@ -26,13 +26,13 @@ public struct ObjCProtocolList32: ObjCProtocolListProtocol { extension ObjCProtocolList32 { public func protocols( in machO: MachOImage - ) -> [ObjCProtocol]? { + ) -> [(MachOImage, ObjCProtocol)]? { _readProtocols(in: machO, pointerType: UInt32.self) } public func protocols( in machO: MachOFile - ) -> [ObjCProtocol]? { + ) -> [(MachOFile, ObjCProtocol)]? { guard !isListOfLists else { assertionFailure() return nil @@ -63,6 +63,8 @@ extension ObjCProtocolList32 { let offset = $0 + numericCast(headerStartOffset) var resolvedOffset = offset + var targetMachO = machO + var fileHandle = machO.fileHandle if let (_cache, _offset) = machO.cacheAndFileOffset( @@ -70,16 +72,23 @@ extension ObjCProtocolList32 { ) { resolvedOffset = _offset fileHandle = _cache.fileHandle + + let unslidAddress = offset + _cache.mainCacheHeader.sharedRegionStart + if !targetMachO.contains(unslidAddress: unslidAddress), + let machO = _cache.machO(containing: unslidAddress) { + targetMachO = machO + } } let layout: ObjCProtocol32.Layout = fileHandle.read( offset: numericCast(resolvedOffset), swapHandler: { _ in } ) - return .init( + let `protocol`: ObjCProtocol = .init( layout: layout, offset: numericCast(offset) - machO.headerStartOffset ) + return (targetMachO, `protocol`) } } } diff --git a/Sources/MachOObjCSection/Model/Protocol/ObjCProtocolList64.swift b/Sources/MachOObjCSection/Model/Protocol/ObjCProtocolList64.swift index 8d9d649..5990fed 100644 --- a/Sources/MachOObjCSection/Model/Protocol/ObjCProtocolList64.swift +++ b/Sources/MachOObjCSection/Model/Protocol/ObjCProtocolList64.swift @@ -26,13 +26,13 @@ public struct ObjCProtocolList64: ObjCProtocolListProtocol { extension ObjCProtocolList64 { public func protocols( in machO: MachOImage - ) -> [ObjCProtocol]? { + ) -> [(MachOImage, ObjCProtocol)]? { _readProtocols(in: machO, pointerType: UInt64.self) } public func protocols( in machO: MachOFile - ) -> [ObjCProtocol]? { + ) -> [(MachOFile, ObjCProtocol)]? { guard !isListOfLists else { assertionFailure() return nil @@ -63,6 +63,8 @@ extension ObjCProtocolList64 { let offset = machO.fileOffset(of: $0) + numericCast(headerStartOffset) var resolvedOffset = offset + var targetMachO = machO + var fileHandle = machO.fileHandle if let (_cache, _offset) = machO.cacheAndFileOffset( @@ -70,16 +72,23 @@ extension ObjCProtocolList64 { ) { resolvedOffset = _offset fileHandle = _cache.fileHandle + + let unslidAddress = offset + _cache.mainCacheHeader.sharedRegionStart + if !targetMachO.contains(unslidAddress: unslidAddress), + let machO = _cache.machO(containing: unslidAddress) { + targetMachO = machO + } } let layout: ObjCProtocol64.Layout = fileHandle.read( offset: numericCast(resolvedOffset), swapHandler: { _ in } ) - return .init( + let `protocol`: ObjCProtocol = .init( layout: layout, offset: numericCast(offset) - machO.headerStartOffset ) + return (targetMachO, `protocol`) } } } diff --git a/Sources/MachOObjCSection/Protocol/Category/ObjCCategoryProtocol.swift b/Sources/MachOObjCSection/Protocol/Category/ObjCCategoryProtocol.swift index 182e865..dd858c9 100644 --- a/Sources/MachOObjCSection/Protocol/Category/ObjCCategoryProtocol.swift +++ b/Sources/MachOObjCSection/Protocol/Category/ObjCCategoryProtocol.swift @@ -3,7 +3,7 @@ // MachOObjCSection // // Created by p-x9 on 2024/12/06 -// +// // import Foundation @@ -24,8 +24,8 @@ public protocol ObjCCategoryProtocol: _FixupResolvable where LayoutField == ObjC init(layout: Layout, offset: Int, isCatlist2: Bool) func name(in machO: MachOFile) -> String? - func `class`(in machO: MachOFile) -> ObjCClass? - func stubClass(in machO: MachOFile) -> ObjCStubClass? + func `class`(in machO: MachOFile) -> (MachOFile, ObjCClass)? + func stubClass(in machO: MachOFile) -> (MachOFile, ObjCStubClass)? func className(in machO: MachOFile) -> String? func instanceMethodList(in machO: MachOFile) -> ObjCMethodList? func classMethodList(in machO: MachOFile) -> ObjCMethodList? @@ -34,8 +34,8 @@ public protocol ObjCCategoryProtocol: _FixupResolvable where LayoutField == ObjC func protocolList(in machO: MachOFile) -> ObjCProtocolList? func name(in machO: MachOImage) -> String? - func `class`(in machO: MachOImage) -> ObjCClass? - func stubClass(in machO: MachOImage) -> ObjCStubClass? + func `class`(in machO: MachOImage) -> (MachOImage, ObjCClass)? + func stubClass(in machO: MachOImage) -> (MachOImage, ObjCStubClass)? func className(in machO: MachOImage) -> String? func instanceMethodList(in machO: MachOImage) -> ObjCMethodList? func classMethodList(in machO: MachOImage) -> ObjCMethodList? @@ -58,8 +58,8 @@ extension ObjCCategoryProtocol { return machO.fileHandle.readString(offset: numericCast(offset)) } - public func `class`(in machO: MachOFile) -> ObjCClass? { - guard let cls = _readClass( + public func `class`(in machO: MachOFile) -> (MachOFile, ObjCClass)? { + guard let (machO, cls) = _readClass( at: numericCast(layout.cls), field: .cls, in: machO @@ -67,10 +67,11 @@ extension ObjCCategoryProtocol { if cls.isStubClass { return nil } - return cls + return (machO, cls) } - public func stubClass(in machO: MachOFile) -> ObjCStubClass? { - guard let cls = _readStubClass( + + public func stubClass(in machO: MachOFile) -> (MachOFile, ObjCStubClass)? { + guard let (machO, cls) = _readStubClass( at: numericCast(layout.cls), field: .cls, in: machO @@ -78,7 +79,7 @@ extension ObjCCategoryProtocol { guard cls.isStubClass else { return nil } - return cls + return (machO, cls) } public func className(in machO: MachOFile) -> String? { @@ -163,38 +164,58 @@ extension ObjCCategoryProtocol { ) } - public func `class`(in machO: MachOImage) -> ObjCClass? { + public func `class`(in machO: MachOImage) -> (MachOImage, ObjCClass)? { guard layout.cls > 0 else { return nil } guard let ptr = UnsafeRawPointer(bitPattern: UInt(layout.cls)) else { return nil } - let offset: Int = numericCast(layout.cls) - Int(bitPattern: machO.ptr) + + var targetMachO = machO + if !targetMachO.contains(ptr: ptr) { + guard let cache = DyldCacheLoaded.current, + let _targetMachO = cache.machO(containing: ptr) else { + return nil + } + targetMachO = _targetMachO + } + + let offset: Int = numericCast(layout.cls) - Int(bitPattern: targetMachO.ptr) let layout = ptr.assumingMemoryBound(to: ObjCClass.Layout.self).pointee let cls: ObjCClass = .init(layout: layout, offset: offset) if cls.isStubClass { return nil } - return cls + return (targetMachO, cls) } - public func stubClass(in machO: MachOImage) -> ObjCStubClass? { + public func stubClass(in machO: MachOImage) -> (MachOImage, ObjCStubClass)? { guard layout.cls > 0 else { return nil } guard let ptr = UnsafeRawPointer(bitPattern: UInt(layout.cls)) else { return nil } - let offset: Int = numericCast(layout.cls) - Int(bitPattern: machO.ptr) + + var targetMachO = machO + if !targetMachO.contains(ptr: ptr) { + guard let cache = DyldCacheLoaded.current, + let _targetMachO = cache.machO(containing: ptr) else { + return nil + } + targetMachO = _targetMachO + } + + let offset: Int = numericCast(layout.cls) - Int(bitPattern: targetMachO.ptr) let layout = ptr.assumingMemoryBound(to: ObjCStubClass.Layout.self).pointee let cls: ObjCStubClass = .init(layout: layout, offset: offset) guard cls.isStubClass else { return nil } - return cls + return (targetMachO, cls) } public func className(in machO: MachOImage) -> String? { - guard let cls = `class`(in: machO) else { + guard let (machO, cls) = `class`(in: machO) else { if let section = machO.sectionNumber(for: .__objc_const), let symbol = machO.symbol( for: offset, inSection: section @@ -261,12 +282,50 @@ extension ObjCCategoryProtocol { } } +extension ObjCCategoryProtocol { + @available(*, deprecated, renamed: "class(in:)", message: "Use `class(in:)` that returns machO that contains class") + public func `class`(in machO: MachOFile) -> ObjCClass? { + guard let (_, cls) = self.class(in: machO) else { + return nil + } + return cls + } + + @available(*, deprecated, renamed: "stubClass(in:)", message: "Use `stubCclass(in:)` that returns machO that contains class") + public func stubClass(in machO: MachOFile) -> ObjCStubClass? { + guard let (_, cls) = self.stubClass(in: machO) else { + return nil + } + return cls + } + + @available(*, deprecated, renamed: "class(in:)", message: "Use `class(in:)` that returns machO that contains class") + func `class`(in machO: MachOImage) -> ObjCClass? { + guard let (targetMachO, cls) = self.class(in: machO) else { return nil } + let diff = Int(bitPattern: targetMachO.ptr) - Int(bitPattern: machO.ptr) + return .init( + layout: cls.layout, + offset: cls.offset + diff + ) + } + + @available(*, deprecated, renamed: "stubClass(in:)", message: "Use `stubCclass(in:)` that returns machO that contains class") + func stubClass(in machO: MachOImage) -> ObjCStubClass? { + guard let (targetMachO, cls) = self.stubClass(in: machO) else { return nil } + let diff = Int(bitPattern: targetMachO.ptr) - Int(bitPattern: machO.ptr) + return .init( + layout: cls.layout, + offset: cls.offset + diff + ) + } +} + extension ObjCCategoryProtocol { private func _readClass( at offset: UInt64, field: LayoutField, in machO: MachOFile - ) -> ObjCClass? { + ) -> (MachOFile, ObjCClass)? { guard offset > 0 else { return nil } var offset: UInt64 = machO.fileOffset( of: numericCast(offset) @@ -277,50 +336,69 @@ extension ObjCCategoryProtocol { } if isBind(field, in: machO) { return nil } + var targetMachO = machO + + var fileHandle = machO.fileHandle var resolvedOffset = offset - if let cache = machO.cache { - guard let _offset = cache.fileOffset(of: offset + cache.mainCacheHeader.sharedRegionStart) else { - return nil - } + if let (_cache, _offset) = machO.cacheAndFileOffset( + fromStart: offset + ) { resolvedOffset = _offset + fileHandle = _cache.fileHandle + + let unslidAddress = offset + _cache.mainCacheHeader.sharedRegionStart + if !targetMachO.contains(unslidAddress: unslidAddress), + let machO = _cache.machO(containing: unslidAddress) { + targetMachO = machO + } } - let layout: ObjCClass.Layout = machO.fileHandle.read(offset: resolvedOffset) - return .init( + let layout: ObjCClass.Layout = fileHandle.read(offset: resolvedOffset) + let cls: ObjCClass = .init( layout: layout, offset: numericCast(offset) - machO.headerStartOffset ) + return (targetMachO, cls) } func _readStubClass( at offset: UInt64, field: LayoutField, in machO: MachOFile - ) -> ObjCStubClass? { + ) -> (MachOFile, ObjCStubClass)? { guard offset > 0 else { return nil } var offset: UInt64 = machO.fileOffset( of: numericCast(offset) ) + numericCast(machO.headerStartOffset) - if let resolved = resolveRebase(field, in: machO) { offset = machO.fileOffset(of: resolved) + numericCast(machO.headerStartOffset) } if isBind(field, in: machO) { return nil } + var targetMachO = machO + + var fileHandle = machO.fileHandle var resolvedOffset = offset - if let cache = machO.cache { - guard let _offset = cache.fileOffset(of: offset + cache.mainCacheHeader.sharedRegionStart) else { - return nil - } + if let (_cache, _offset) = machO.cacheAndFileOffset( + fromStart: offset + ) { resolvedOffset = _offset + fileHandle = _cache.fileHandle + + let unslidAddress = offset + _cache.mainCacheHeader.sharedRegionStart + if !targetMachO.contains(unslidAddress: unslidAddress), + let machO = _cache.machO(containing: unslidAddress) { + targetMachO = machO + } } - let layout: ObjCStubClass.Layout = machO.fileHandle.read(offset: resolvedOffset) - return .init( + let layout: ObjCStubClass.Layout = fileHandle.read(offset: resolvedOffset) + let cls: ObjCStubClass = .init( layout: layout, offset: numericCast(offset) - machO.headerStartOffset ) + return (targetMachO, cls) } private func _readClassName( @@ -330,13 +408,13 @@ extension ObjCCategoryProtocol { ) -> String? { guard offset > 0 else { return nil } - if let cls = _readClass( + if let (targetMachO, cls) = _readClass( at: offset, field: field, in: machO ), !cls.isStubClass , - let data = cls.classROData(in: machO) { - return data.name(in: machO) + let data = cls.classROData(in: targetMachO) { + return data.name(in: targetMachO) } if let bindSymbolName = resolveBind(field, in: machO) { @@ -360,7 +438,7 @@ extension ObjCCategoryProtocol { ) + numericCast(machO.headerStartOffset) if let resolved = resolveRebase(field, in: machO), - resolved != offset { + resolved != offset { offset = machO.fileOffset(of: resolved) + numericCast(machO.headerStartOffset) } // if isBind(\.baseMethods, in: machO) { return nil } diff --git a/Sources/MachOObjCSection/Protocol/Class/ObjCClassProtocol.swift b/Sources/MachOObjCSection/Protocol/Class/ObjCClassProtocol.swift index 21907cc..eb1e6b9 100644 --- a/Sources/MachOObjCSection/Protocol/Class/ObjCClassProtocol.swift +++ b/Sources/MachOObjCSection/Protocol/Class/ObjCClassProtocol.swift @@ -21,15 +21,15 @@ public protocol ObjCClassProtocol: _FixupResolvable where LayoutField == ObjCCla @_spi(Core) init(layout: Layout, offset: Int) - func metaClass(in machO: MachOFile) -> Self? - func superClass(in machO: MachOFile) -> Self? + func metaClass(in machO: MachOFile) -> (MachOFile, Self)? + func superClass(in machO: MachOFile) -> (MachOFile, Self)? func superClassName(in machO: MachOFile) -> String? func classROData(in machO: MachOFile) -> ClassROData? func hasRWPointer(in machO: MachOImage) -> Bool - func metaClass(in machO: MachOImage) -> Self? - func superClass(in machO: MachOImage) -> Self? + func metaClass(in machO: MachOImage) -> (MachOImage, Self)? + func superClass(in machO: MachOImage) -> (MachOImage, Self)? func superClassName(in machO: MachOImage) -> String? func classROData(in machO: MachOImage) -> ClassROData? func classRWData(in machO: MachOImage) -> ClassRWData? @@ -65,7 +65,7 @@ extension ObjCClassProtocol { } extension ObjCClassProtocol { - public func metaClass(in machO: MachOFile) -> Self? { + public func metaClass(in machO: MachOFile) -> (MachOFile, Self)? { _readClass( at: numericCast(layout.isa), field: .isa, @@ -73,7 +73,7 @@ extension ObjCClassProtocol { ) } - public func superClass(in machO: MachOFile) -> Self? { + public func superClass(in machO: MachOFile) -> (MachOFile, Self)? { _readClass( at: numericCast(layout.superclass), field: .superclass, @@ -91,28 +91,54 @@ extension ObjCClassProtocol { } extension ObjCClassProtocol { - public func metaClass(in machO: MachOImage) -> Self? { + public func metaClass(in machO: MachOImage) -> (MachOImage, Self)? { guard layout.isa > 0 else { return nil } guard let ptr = UnsafeRawPointer(bitPattern: UInt(layout.isa)) else { return nil } + + var targetMachO = machO + if !targetMachO.contains(ptr: ptr) { + guard let cache = DyldCacheLoaded.current, + let _targetMachO = cache.machO(containing: ptr) else { + return nil + } + targetMachO = _targetMachO + } + + let offset: Int = numericCast(layout.isa) - Int(bitPattern: targetMachO.ptr) + let layout = ptr.assumingMemoryBound(to: Layout.self).pointee - let offset: Int = numericCast(layout.isa) - Int(bitPattern: machO.ptr) - return .init(layout: layout, offset: offset) + let cls: Self = .init(layout: layout, offset: offset) + + return (targetMachO, cls) } - public func superClass(in machO: MachOImage) -> Self? { + public func superClass(in machO: MachOImage) -> (MachOImage, Self)? { guard layout.superclass > 0 else { return nil } guard let ptr = UnsafeRawPointer(bitPattern: UInt(layout.superclass)) else { return nil } + + var targetMachO = machO + if !targetMachO.contains(ptr: ptr) { + guard let cache = DyldCacheLoaded.current, + let _targetMachO = cache.machO(containing: ptr) else { + return nil + } + targetMachO = _targetMachO + } + + let offset: Int = numericCast(layout.superclass) - Int(bitPattern: targetMachO.ptr) + let layout = ptr.assumingMemoryBound(to: Layout.self).pointee - let offset: Int = numericCast(layout.superclass) - Int(bitPattern: machO.ptr) - return .init(layout: layout, offset: offset) + let cls: Self = .init(layout: layout, offset: offset) + + return (targetMachO, cls) } public func superClassName(in machO: MachOImage) -> String? { - guard let superCls = superClass(in: machO) else { + guard let (machO, superCls) = superClass(in: machO) else { return nil } @@ -133,12 +159,50 @@ extension ObjCClassProtocol { } } +extension ObjCClassProtocol { + @available(*, deprecated, renamed: "metaClass(in:)", message: "Use `metaClass(in:)` that returns machO that contains class") + public func metaClass(in machO: MachOFile) -> Self? { + guard let (_, cls) = self.metaClass(in: machO) else { + return nil + } + return cls + } + + @available(*, deprecated, renamed: "superClass(in:)", message: "Use `superClass(in:)` that returns machO that contains class") + public func superClass(in machO: MachOFile) -> Self? { + guard let (_, cls) = self.superClass(in: machO) else { + return nil + } + return cls + } + + @available(*, deprecated, renamed: "metaClass(in:)", message: "Use `metaClass(in:)` that returns machO that contains class") + func metaClass(in machO: MachOImage) -> Self? { + guard let (targetMachO, cls) = self.metaClass(in: machO) else { return nil } + let diff = Int(bitPattern: targetMachO.ptr) - Int(bitPattern: machO.ptr) + return .init( + layout: cls.layout, + offset: cls.offset + diff + ) + } + + @available(*, deprecated, renamed: "superClass(in:)", message: "Use `superClass(in:)` that returns machO that contains class") + func superClass(in machO: MachOImage) -> Self? { + guard let (targetMachO, cls) = self.superClass(in: machO) else { return nil } + let diff = Int(bitPattern: targetMachO.ptr) - Int(bitPattern: machO.ptr) + return .init( + layout: cls.layout, + offset: cls.offset + diff + ) + } +} + extension ObjCClassProtocol { private func _readClass( at offset: UInt64, field: LayoutField, in machO: MachOFile - ) -> Self? { + ) -> (MachOFile, Self)? { guard offset > 0 else { return nil } var offset: UInt64 = machO.fileOffset( of: numericCast(offset) @@ -149,19 +213,29 @@ extension ObjCClassProtocol { } if isBind(field, in: machO) { return nil } + var targetMachO = machO + + var fileHandle = machO.fileHandle var resolvedOffset = offset - if let cache = machO.cache { - guard let _offset = cache.fileOffset(of: offset + cache.mainCacheHeader.sharedRegionStart) else { - return nil - } + if let (_cache, _offset) = machO.cacheAndFileOffset( + fromStart: offset + ) { resolvedOffset = _offset + fileHandle = _cache.fileHandle + + let unslidAddress = offset + _cache.mainCacheHeader.sharedRegionStart + if !targetMachO.contains(unslidAddress: unslidAddress), + let machO = _cache.machO(containing: unslidAddress) { + targetMachO = machO + } } - let layout: Layout = machO.fileHandle.read(offset: resolvedOffset) - return .init( + let layout: Layout = fileHandle.read(offset: resolvedOffset) + let cls: Self = .init( layout: layout, offset: numericCast(offset) - machO.headerStartOffset ) + return (targetMachO, cls) } private func _readClassName( @@ -171,12 +245,12 @@ extension ObjCClassProtocol { ) -> String? { guard offset > 0 else { return nil } - if let cls = _readClass( + if let (targetMachO, cls) = _readClass( at: offset, field: field, in: machO - ), let data = cls.classROData(in: machO) { - return data.name(in: machO) + ), let data = cls.classROData(in: targetMachO) { + return data.name(in: targetMachO) } if let bindSymbolName = resolveBind(field, in: machO) { diff --git a/Sources/MachOObjCSection/Protocol/Protocol/ObjCProtocolListProtocol.swift b/Sources/MachOObjCSection/Protocol/Protocol/ObjCProtocolListProtocol.swift index 4439c71..189db04 100644 --- a/Sources/MachOObjCSection/Protocol/Protocol/ObjCProtocolListProtocol.swift +++ b/Sources/MachOObjCSection/Protocol/Protocol/ObjCProtocolListProtocol.swift @@ -3,7 +3,7 @@ // // // Created by p-x9 on 2024/07/19 -// +// // import Foundation @@ -23,8 +23,8 @@ public protocol ObjCProtocolListProtocol { @_spi(Core) init(ptr: UnsafeRawPointer, offset: Int) - func protocols(in machO: MachOImage) -> [ObjCProtocol]? - func protocols(in machO: MachOFile) -> [ObjCProtocol]? + func protocols(in machO: MachOImage) -> [(MachOImage, ObjCProtocol)]? + func protocols(in machO: MachOFile) -> [(MachOFile, ObjCProtocol)]? } extension ObjCProtocolListProtocol { @@ -37,8 +37,7 @@ extension ObjCProtocolListProtocol { func _readProtocols( in machO: MachOImage, pointerType: Pointer.Type - ) -> [ObjCProtocol]? { - // TODO: Support listOfLists + ) -> [(MachOImage, ObjCProtocol)]? { guard !isListOfLists else { return nil } let ptr = machO.ptr.advanced(by: offset) @@ -57,10 +56,21 @@ extension ObjCProtocolListProtocol { let layout = ptr .assumingMemoryBound(to: ObjCProtocol.Layout.self) .pointee - return .init( + + var targetMachO = machO + if !targetMachO.contains(ptr: ptr) { + guard let cache = DyldCacheLoaded.current, + let _targetMachO = cache.machO(containing: ptr) else { + return nil + } + targetMachO = _targetMachO + } + + let `protocol`: ObjCProtocol = .init( layout: layout, - offset: Int(bitPattern: ptr) - Int(bitPattern: machO.ptr) + offset: Int(bitPattern: ptr) - Int(bitPattern: targetMachO.ptr) ) + return (targetMachO, `protocol`) } } } diff --git a/Sources/MachOObjCSection/Support/ObjCDump.swift b/Sources/MachOObjCSection/Support/ObjCDump.swift index 78580d3..10db56d 100644 --- a/Sources/MachOObjCSection/Support/ObjCDump.swift +++ b/Sources/MachOObjCSection/Support/ObjCDump.swift @@ -3,7 +3,7 @@ // // // Created by p-x9 on 2024/09/28 -// +// // import Foundation @@ -73,7 +73,7 @@ extension ObjCProtocolProtocol { let protocolList = protocolList(in: machO) let protocols = protocolList? .protocols(in: machO)? - .compactMap { $0.info(in: machO) } ?? [] + .compactMap { $1.info(in: $0) } ?? [] let classPropertiesList = classPropertyList(in: machO) let classProperties = classPropertiesList? @@ -129,7 +129,7 @@ extension ObjCProtocolProtocol { let protocolList = protocolList(in: machO) let protocols = protocolList? .protocols(in: machO)? - .compactMap { $0.info(in: machO) } ?? [] + .compactMap { $1.info(in: $0) } ?? [] let classPropertiesList = classPropertyList(in: machO) let classProperties = classPropertiesList? @@ -182,21 +182,21 @@ extension ObjCProtocolProtocol { extension ObjCClassProtocol { public func info(in machO: MachOFile) -> ObjCClassInfo? { guard let data = classROData(in: machO), - let meta = metaClass(in: machO), - let metaData = meta.classROData(in: machO), + let (targetMachO, meta) = metaClass(in: machO), + let metaData = meta.classROData(in: targetMachO), let name = data.name(in: machO) else { return nil } let protocolList = data.protocolList(in: machO) var protocols = protocolList? .protocols(in: machO)? - .compactMap { $0.info(in: machO) } ?? [] + .compactMap { $1.info(in: $0) } ?? [] if let relative = data.protocolRelativeListList(in: machO) { protocols = relative.lists(in: machO) .filter({ $0.0.imagePath == machO.imagePath }) .flatMap { machO, list in list.protocols(in: machO)? - .compactMap { $0.info(in: machO) } ?? [] + .compactMap { $1.info(in: $0) } ?? [] } } @@ -233,26 +233,26 @@ extension ObjCClassProtocol { } // Meta - let classPropertiesList = metaData.propertyList(in: machO) + let classPropertiesList = metaData.propertyList(in: targetMachO) var classProperties = classPropertiesList? - .properties(in: machO) + .properties(in: targetMachO) .compactMap { $0.info(isClassProperty: true) } ?? [] - if let relative = metaData.propertyRelativeListList(in: machO) { - classProperties = relative.lists(in: machO) - .filter { $0.0.imagePath == machO.imagePath } + if let relative = metaData.propertyRelativeListList(in: targetMachO) { + classProperties = relative.lists(in: targetMachO) + .filter { $0.0.imagePath == targetMachO.imagePath } .flatMap { machO, list in list.properties(in: machO) .compactMap { $0.info(isClassProperty: true) } } } - let classMethodsList = metaData.methodList(in: machO) + let classMethodsList = metaData.methodList(in: targetMachO) var classMethods = classMethodsList? - .methods(in: machO)? + .methods(in: targetMachO)? .compactMap { $0.info(isClassMethod: true) } ?? [] - if let relative = metaData.methodRelativeListList(in: machO) { - classMethods = relative.lists(in: machO) - .filter({ $0.0.imagePath == machO.imagePath }) + if let relative = metaData.methodRelativeListList(in: targetMachO) { + classMethods = relative.lists(in: targetMachO) + .filter({ $0.0.imagePath == targetMachO.imagePath }) .flatMap { machO, list in list.methods(in: machO)? .compactMap { $0.info(isClassMethod: true) } ?? [] @@ -277,7 +277,7 @@ extension ObjCClassProtocol { } public func info(in machO: MachOImage) -> ObjCClassInfo? { - guard let meta = metaClass(in: machO) else { + guard let (targetMachO, meta) = metaClass(in: machO) else { return nil } @@ -299,13 +299,13 @@ extension ObjCClassProtocol { return nil } - if let _data = meta.classROData(in: machO) { + if let _data = meta.classROData(in: targetMachO) { metaData = _data - } else if let rw = meta.classRWData(in: machO) { - if let _data = rw.classROData(in: machO) { + } else if let rw = meta.classRWData(in: targetMachO) { + if let _data = rw.classROData(in: targetMachO) { metaData = _data - } else if let ext = rw.ext(in: machO), - let _data = ext.classROData(in: machO) { + } else if let ext = rw.ext(in: targetMachO), + let _data = ext.classROData(in: targetMachO) { metaData = _data } else { return nil @@ -321,13 +321,13 @@ extension ObjCClassProtocol { let protocolList = data.protocolList(in: machO) var protocols = protocolList? .protocols(in: machO)? - .compactMap { $0.info(in: machO) } ?? [] + .compactMap { $1.info(in: $0) } ?? [] if let relative = data.protocolRelativeListList(in: machO) { protocols = relative.lists(in: machO) - .filter({ $0.0.path == machO.path }) + .filter({ $0.0.ptr == machO.ptr }) .flatMap { machO, list in list.protocols(in: machO)? - .compactMap { $0.info(in: machO) } ?? [] + .compactMap { $1.info(in: $0) } ?? [] } } @@ -364,26 +364,26 @@ extension ObjCClassProtocol { } // Meta - let classPropertiesList = metaData.propertyList(in: machO) + let classPropertiesList = metaData.propertyList(in: targetMachO) var classProperties = classPropertiesList? - .properties(in: machO) + .properties(in: targetMachO) .compactMap { $0.info(isClassProperty: true) } ?? [] - if let relative = metaData.propertyRelativeListList(in: machO) { - classProperties = relative.lists(in: machO) - .filter { $0.0.ptr == machO.ptr } + if let relative = metaData.propertyRelativeListList(in: targetMachO) { + classProperties = relative.lists(in: targetMachO) + .filter { $0.0.ptr == targetMachO.ptr } .flatMap { machO, list in list.properties(in: machO) .compactMap { $0.info(isClassProperty: true) } } } - let classMethodsList = metaData.methodList(in: machO) + let classMethodsList = metaData.methodList(in: targetMachO) var classMethods = classMethodsList? - .methods(in: machO) + .methods(in: targetMachO) .compactMap { $0.info(isClassMethod: true) } ?? [] - if let relative = metaData.methodRelativeListList(in: machO) { - classMethods = relative.lists(in: machO) - .filter({ $0.0.ptr == machO.ptr }) + if let relative = metaData.methodRelativeListList(in: targetMachO) { + classMethods = relative.lists(in: targetMachO) + .filter({ $0.0.ptr == targetMachO.ptr }) .flatMap { machO, list in list.methods(in: machO) .compactMap { $0.info(isClassMethod: true) } @@ -419,7 +419,7 @@ extension ObjCCategoryProtocol { let protocolList = protocolList(in: machO) let protocols = protocolList? .protocols(in: machO)? - .compactMap { $0.info(in: machO) } ?? [] + .compactMap { $1.info(in: $0) } ?? [] // Instance let propertiesList = instancePropertyList(in: machO) @@ -463,7 +463,7 @@ extension ObjCCategoryProtocol { let protocolList = protocolList(in: machO) let protocols = protocolList? .protocols(in: machO)? - .compactMap { $0.info(in: machO) } ?? [] + .compactMap { $1.info(in: $0) } ?? [] // Instance let propertiesList = instancePropertyList(in: machO)