├── .spi.yml ├── MachOKit.docc ├── MachOKit.md └── MachOKitC.md ├── Makefile ├── Sources ├── MachOKit │ ├── Model │ │ ├── Endian.swift │ │ ├── AotCache │ │ │ ├── AotCodeFragmentType.swift │ │ │ ├── BranchData │ │ │ │ ├── AotBranchDataHeader.swift │ │ │ │ ├── AotBranchDataIndexEntry.swift │ │ │ │ └── AotBranchData.swift │ │ │ ├── InstructionMap │ │ │ │ ├── AotInstructionMapHeader.swift │ │ │ │ ├── AotInstructionMapIndexEntry.swift │ │ │ │ └── AotInstructionMap.swift │ │ │ ├── AotCodeFragment.swift │ │ │ └── AotCacheCodeFragment.swift │ │ ├── DyldCache │ │ │ ├── ProgramOffset.swift │ │ │ ├── Loader │ │ │ │ ├── ObjCBinaryInfo.swift │ │ │ │ ├── LoaderRef.swift │ │ │ │ └── SectionLocations.swift │ │ │ ├── DyldCacheTproMappingInfo.swift │ │ │ ├── DyldCachePrewarmingEntry.swift │ │ │ ├── DylibIndex.swift │ │ │ ├── DyldCacheFunctionVariantEntry.swift │ │ │ ├── SwiftOptimization.swift │ │ │ ├── DylibsTrieNodeContent.swift │ │ │ ├── ProgramsTrieNodeContent.swift │ │ │ ├── DyldCacheMappingInfo.swift │ │ │ ├── SlideInfo │ │ │ │ ├── DyldCacheSlideInfo.swift │ │ │ │ ├── DyldCacheSlideInfo3.swift │ │ │ │ ├── DyldCacheSlideInfo5.swift │ │ │ │ ├── DyldCacheSlideInfo1.swift │ │ │ │ └── DyldCacheSlideInfo4.swift │ │ │ ├── DyldCacheDynamicData.swift │ │ │ ├── ObjCOptimization │ │ │ │ ├── ObjCHeaderInfoRW.swift │ │ │ │ └── ObjCOptimizationFlags.swift │ │ │ ├── DyldCacheLocalSymbolsEntry.swift │ │ │ ├── DyldCacheImageInfo.swift │ │ │ ├── DyldCacheImageTextInfo.swift │ │ │ ├── DyldCacheFunctionVariantInfo.swift │ │ │ └── DyldCachePrewarming.swift │ │ ├── Bind │ │ │ ├── ClassicBindType.swift │ │ │ ├── BindOperationsKind.swift │ │ │ ├── BindType.swift │ │ │ └── BindSpecial.swift │ │ ├── DyldChain │ │ │ ├── DyldChainedSymbolsFormat.swift │ │ │ ├── DyldChainedPage.swift │ │ │ ├── DyldChainedStartsInImage.swift │ │ │ ├── DyldChainedImportFormat.swift │ │ │ ├── DyldChainedStartsOffsets.swift │ │ │ ├── DyldChainedStartsInSegment.swift │ │ │ └── DyldChainedFixupsHeader.swift │ │ ├── StringTableEntry.swift │ │ ├── ClassicBindingSymbol.swift │ │ ├── Relocation │ │ │ ├── RelocationLength.swift │ │ │ ├── ScatteredRelocationInfo.swift │ │ │ ├── RelocationInfo.swift │ │ │ └── Relocation.swift │ │ ├── Codesign │ │ │ ├── CodeSignBlobIndex.swift │ │ │ ├── CodeSignSpecialSlotType.swift │ │ │ ├── CodeDirectory │ │ │ │ ├── CodeSignCodeDirectory+scatter.swift │ │ │ │ ├── CodeSignCodeDirectory+codeLimit64.swift │ │ │ │ ├── CodeSignCodeDirectory+executableSegment.swift │ │ │ │ ├── CodeSignCodeDirectory+teamID.swift │ │ │ │ └── CodeSignCodeDirectory+runtime.swift │ │ │ ├── CodeSignHashType.swift │ │ │ ├── CodeSignGenericBlob.swift │ │ │ └── CodeSignSuperBlob.swift │ │ ├── DependedDylib.swift │ │ ├── FunctionStart.swift │ │ ├── ExportedSymbol.swift │ │ ├── IndirectSymbol.swift │ │ ├── Symbol │ │ │ ├── Nlist.swift │ │ │ ├── SymbolLibraryOrdinalType.swift │ │ │ ├── SymbolType.swift │ │ │ ├── SymbolFlags.swift │ │ │ └── SymbolReferenceFlag.swift │ │ ├── Rebase │ │ │ └── RebaseType.swift │ │ ├── Export │ │ │ └── ExportSymbolKind.swift │ │ └── DataInCodeEntry.swift │ ├── Util │ │ ├── global.swift │ │ ├── exported.swift │ │ ├── TrieTree │ │ │ ├── Protocol │ │ │ │ └── TrieNodeContent.swift │ │ │ ├── MemoryTrieTree.swift │ │ │ └── DataTrieTree.swift │ │ ├── BitFlags.swift │ │ ├── SwiftDemangle.swift │ │ └── Sequence │ │ │ ├── MemorySequence.swift │ │ │ └── DataSequence.swift │ ├── Extension │ │ ├── UUID+.swift │ │ ├── UnsafeRawPointer+.swift │ │ ├── FixedWidthInteger+.swift │ │ ├── Data+.swift │ │ └── UnsafePointer+.swift │ ├── Header │ │ ├── DyldCacheHeader │ │ │ └── DyldCacheType.swift │ │ ├── FatHeader │ │ │ ├── FatArch.swift │ │ │ └── FatHeader.swift │ │ ├── AotCacheHeader │ │ │ └── AotCacheHeader.swift │ │ ├── MachHeader │ │ │ ├── MachHeader.swift │ │ │ └── FileType.swift │ │ ├── CPU.swift │ │ └── Magic.swift │ ├── DyldCache+host.swift │ ├── LoadCommand │ │ ├── LoadCommandInfo.swift │ │ ├── Model │ │ │ ├── BuildToolVersion.swift │ │ │ ├── Section+Flags.swift │ │ │ ├── Dylib.swift │ │ │ ├── ThreadState.swift │ │ │ ├── Version.swift │ │ │ ├── DylibUseFlags.swift │ │ │ └── Tool.swift │ │ ├── UUIDCommand.swift │ │ ├── SourceVersionCommand.swift │ │ ├── VersionMinCommand.swift │ │ ├── EntryPointCommand.swift │ │ ├── NoteCommand.swift │ │ ├── RpathCommand.swift │ │ ├── DylinkerCommand.swift │ │ ├── SubFrameworkCommand.swift │ │ ├── TargetTripleCommand.swift │ │ ├── FilesetEntryCommand.swift │ │ ├── EncryptionInfoCommand.swift │ │ ├── LinkerOptionCommand.swift │ │ ├── AotMetadataCommand.swift │ │ ├── BuildVersionCommand.swift │ │ ├── DylibUseCommand.swift │ │ ├── SegmentCommand+Flags.swift │ │ └── DylibCommand.swift │ ├── DyldCacheLoaded+static.swift │ ├── FullDyldCache+host.swift │ ├── Protocol │ │ ├── SymbolProtocol.swift │ │ ├── StringTable.swift │ │ ├── DyldChainedImportProtocol.swift │ │ ├── DyldCacheLocalSymbolsEntryProtocol.swift │ │ ├── DyldChainedFixupsProtocol.swift │ │ ├── LayoutWrapper.swift │ │ └── LoadCommandsProtocol.swift │ ├── MachOKit.swift │ ├── MachOImage+LoadCommands.swift │ ├── FatFile.swift │ ├── MachOFile+BindOperations.swift │ ├── MachOFile+RebaseOperations.swift │ ├── MachOFile+LoadCommands.swift │ ├── MachOImage+RebaseOperations.swift │ ├── AotCache.swift │ ├── MachOImage+BindOperations.swift │ └── AotCache+CodeFragments.swift └── MachOKitC │ └── include │ ├── dyld_cache.h │ ├── swift.h │ ├── core_foundation.h │ ├── thread_state.h │ └── backports.h ├── .gitignore ├── .swiftpm └── xcode │ └── package.xcworkspace │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── scripts ├── docc-preview.sh ├── docc.sh └── generate-symbols.sh ├── Package.resolved ├── Tests └── MachOKitTests │ └── MachOKitTests.swift ├── .github └── workflows │ ├── docc.yml │ ├── ci.yml │ ├── update-prebuild-spm.yml │ └── release.yml └── LICENSE /.spi.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | external_links: 3 | documentation: "https://p-x9.github.io/MachOKit/documentation/machokit/" 4 | -------------------------------------------------------------------------------- /MachOKit.docc/MachOKit.md: -------------------------------------------------------------------------------- 1 | # ``MachOKit`` 2 | 3 | 🔬 A Swift library for parsing MachO files to obtain various information. 4 | -------------------------------------------------------------------------------- /MachOKit.docc/MachOKitC.md: -------------------------------------------------------------------------------- 1 | # ``MachOKitC`` 2 | 3 | 🔬 A Swift library for parsing MachO files to obtain various information. 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: xcframework 2 | xcframework: 3 | bash scripts/xcframework.sh 4 | 5 | .PHONY: docc 6 | docc: 7 | bash scripts/docc.sh 8 | 9 | .PHONY: docc-preview 10 | docc-preview: 11 | bash scripts/docc-preview.sh 12 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/Endian.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Endian.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/07/09 6 | // 7 | // 8 | 9 | public enum Endian: Sendable { 10 | case little 11 | case big 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/configuration/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | 10 | XCFrameworks/ 11 | docs/ 12 | symbol-graphs/ -------------------------------------------------------------------------------- /Sources/MachOKit/Util/global.swift: -------------------------------------------------------------------------------- 1 | // 2 | // global.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/02/17. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public func autoBitCast(_ x: T) -> U { 12 | unsafeBitCast(x, to: U.self) 13 | } 14 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/AotCache/AotCodeFragmentType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AotCodeFragmentType.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/12/15 6 | // 7 | // 8 | 9 | public enum AotCodeFragmentType: UInt32 { 10 | case normal 11 | case runtime 12 | } 13 | -------------------------------------------------------------------------------- /Sources/MachOKit/Util/exported.swift: -------------------------------------------------------------------------------- 1 | // 2 | // exported.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/11/29. 6 | // 7 | // 8 | 9 | #if canImport(MachO) 10 | @_documentation(visibility: internal) 11 | @_exported 12 | import MachO 13 | #endif 14 | 15 | @_exported 16 | import MachOKitC 17 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldCache/ProgramOffset.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProgramOffset.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/07/09 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct ProgramOffset: Sendable { 12 | public let name: String 13 | public let offset: UInt32 14 | } 15 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/Bind/ClassicBindType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ClassicBindType.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/03/04 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public enum ClassicBindType: Sendable { 12 | case relocation(RelocationType) 13 | case pointer 14 | } 15 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldChain/DyldChainedSymbolsFormat.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldChainedSymbolsFormat.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/01/11. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public enum DyldChainedSymbolsFormat: UInt32, Sendable { 12 | case uncompressed 13 | case zlibCompressed 14 | } 15 | -------------------------------------------------------------------------------- /Sources/MachOKit/Extension/UUID+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UUID+.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/10/19 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | extension UUID { 12 | // swiftlint:disable:next force_unwrapping 13 | static let zero: UUID = .init(uuidString: "00000000-0000-0000-0000-000000000000")! 14 | } 15 | -------------------------------------------------------------------------------- /scripts/docc-preview.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | TARGET='MachOKit' 4 | 5 | preview_docc() { 6 | mkdir -p docs 7 | 8 | $(xcrun --find docc) preview \ 9 | "./${TARGET}.docc" \ 10 | --additional-symbol-graph-dir symbol-graphs \ 11 | --output-path "docs" 12 | } 13 | 14 | sh ./scripts/generate-symbols.sh 15 | 16 | preview_docc 17 | -------------------------------------------------------------------------------- /Sources/MachOKit/Extension/UnsafeRawPointer+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UnsafeRawPointer+.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/11/28. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | extension UnsafeRawPointer { 12 | func autoBoundPointee() -> Out { 13 | bindMemory(to: Out.self, capacity: 1).pointee 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldCache/Loader/ObjCBinaryInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ObjCBinaryInfo.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2024/11/16 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct ObjCBinaryInfo: LayoutWrapper, Sendable { 12 | public typealias Layout = objc_binary_info 13 | 14 | public var layout: Layout 15 | } 16 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "pins" : [ 3 | { 4 | "identity" : "swift-fileio", 5 | "kind" : "remoteSourceControl", 6 | "location" : "https://github.com/p-x9/swift-fileio.git", 7 | "state" : { 8 | "revision" : "63daf8e8402789339ccc5a19d198ff0fcafd29d9", 9 | "version" : "0.11.0" 10 | } 11 | } 12 | ], 13 | "version" : 2 14 | } 15 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/AotCache/BranchData/AotBranchDataHeader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AotBranchDataHeader.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/12/18 6 | // 7 | // 8 | 9 | import MachOKitC 10 | 11 | public struct AotBranchDataHeader: LayoutWrapper { 12 | public typealias Layout = aot_branch_data_header 13 | 14 | public var layout: Layout 15 | } 16 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/StringTableEntry.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StringTableEntry.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/04. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct StringTableEntry: Codable, Equatable, Sendable { 12 | public let string: String 13 | /// Offset from the beginning of the string table 14 | public let offset: Int 15 | } 16 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/AotCache/InstructionMap/AotInstructionMapHeader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AotInstructionMapHeader.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/12/16 6 | // 7 | // 8 | 9 | import MachOKitC 10 | 11 | public struct AotInstructionMapHeader: LayoutWrapper { 12 | public typealias Layout = aot_instruction_map_header 13 | 14 | public var layout: Layout 15 | } 16 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldCache/DyldCacheTproMappingInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldCacheTproMappingInfo.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/02/27 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct DyldCacheTproMappingInfo: LayoutWrapper, Sendable { 12 | public typealias Layout = dyld_cache_tpro_mapping_info 13 | 14 | public var layout: Layout 15 | } 16 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/ClassicBindingSymbol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ClassicBindingSymbol.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/03/04 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct ClassicBindingSymbol: Sendable { 12 | public let type: ClassicBindType 13 | public let address: UInt 14 | public let symbolIndex: Int 15 | public let addend: Int 16 | } 17 | -------------------------------------------------------------------------------- /Sources/MachOKit/Util/TrieTree/Protocol/TrieNodeContent.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TrieNodeContent.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/07/05 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol TrieNodeContent { 12 | static func read( 13 | basePointer: UnsafePointer, 14 | trieSize: Int, 15 | nextOffset: inout Int 16 | ) -> Self? 17 | } 18 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/AotCache/InstructionMap/AotInstructionMapIndexEntry.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AotInstructionMapIndexEntry.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/12/16 6 | // 7 | // 8 | 9 | import MachOKitC 10 | 11 | public struct AotInstructionMapIndexEntry: LayoutWrapper { 12 | public typealias Layout = aot_instruction_map_index_entry 13 | 14 | public var layout: Layout 15 | } 16 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldCache/DyldCachePrewarmingEntry.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldCachePrewarmingEntry.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/05/13 6 | // 7 | // 8 | 9 | import Foundation 10 | import MachOKitC 11 | 12 | public struct DyldCachePrewarmingEntry: LayoutWrapper, Sendable { 13 | public typealias Layout = dyld_prewarming_entry 14 | 15 | public var layout: Layout 16 | } 17 | -------------------------------------------------------------------------------- /scripts/docc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | TARGET='MachOKit' 4 | REPO_NAME='MachOKit' 5 | 6 | generate_docc() { 7 | mkdir -p docs 8 | 9 | $(xcrun --find docc) convert \ 10 | "./${TARGET}.docc" \ 11 | --output-path "docs" \ 12 | --hosting-base-path "${REPO_NAME}" \ 13 | --additional-symbol-graph-dir ./symbol-graphs 14 | } 15 | 16 | sh ./scripts/generate-symbols.sh 17 | 18 | generate_docc 19 | -------------------------------------------------------------------------------- /Sources/MachOKit/Header/DyldCacheHeader/DyldCacheType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldCacheType.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/01/14. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public enum DyldCacheType: UInt64, Sendable { 12 | case development 13 | case production 14 | case multiCache 15 | } 16 | 17 | public enum DyldCacheSubType: UInt32, Sendable { 18 | case development 19 | case production 20 | } 21 | -------------------------------------------------------------------------------- /Tests/MachOKitTests/MachOKitTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import MachOKit 3 | 4 | final class MachOKitTests: XCTestCase { 5 | func testExample() throws { 6 | // XCTest Documentation 7 | // https://developer.apple.com/documentation/xctest 8 | 9 | // Defining Test Cases and Test Methods 10 | // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sources/MachOKit/DyldCache+host.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import MachOKitC 3 | 4 | extension DyldCache { 5 | public static var host: DyldCache? { 6 | #if canImport(Darwin) 7 | guard let ptr = dyld_shared_cache_file_path() else { 8 | return nil 9 | } 10 | let url: URL = .init(fileURLWithPath: .init(cString: ptr)) 11 | return try? DyldCache(url: url) 12 | #else 13 | return nil 14 | #endif 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/MachOKit/Extension/FixedWidthInteger+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FixedWidthInteger+.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2024/11/20 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | extension FixedWidthInteger { 12 | var uleb128Size: Int { 13 | var value = self 14 | var result = 0 15 | 16 | repeat { 17 | value = value >> 7 18 | result += 1 19 | } while value != 0 20 | 21 | return result 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/MachOKit/LoadCommand/LoadCommandInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoadCommandInfo.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/11/28. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct LoadCommandInfo: LoadCommandWrapper { 12 | public var layout: Layout 13 | public var offset: Int // offset from mach header trailing 14 | 15 | init(_ layout: Layout, offset: Int) { 16 | self.layout = layout 17 | self.offset = offset 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Sources/MachOKit/LoadCommand/Model/BuildToolVersion.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BuildToolVersion.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/11/29. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct BuildToolVersion: LayoutWrapper, Sendable { 12 | public var layout: build_tool_version 13 | 14 | public var tool: Tool? { 15 | .init(rawValue: Int32(layout.tool)) 16 | } 17 | 18 | public var version: Version { 19 | .init(layout.version) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldChain/DyldChainedPage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldChainedPage.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/02/17. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct DyldChainedPage: Sendable { 12 | public let offset: UInt16 13 | public let index: Int 14 | 15 | public var isNone: Bool { 16 | offset == DYLD_CHAINED_PTR_START_NONE 17 | } 18 | 19 | public var isMulti: Bool { 20 | offset & UInt16(DYLD_CHAINED_PTR_START_MULTI) > 0 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldCache/DylibIndex.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DylibIndex.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/07/07 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /// Index/name pairs, obtained from the Dylibs trie, present in the dyld cache. 12 | /// 13 | /// If an alias for dylib exists, there may be another element with an equal Index in trie. 14 | public struct DylibIndex: Sendable { 15 | // Dylib name 16 | public let name: String 17 | /// Dylib index 18 | public let index: UInt32 19 | } 20 | -------------------------------------------------------------------------------- /Sources/MachOKit/DyldCacheLoaded+static.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldCacheLoaded+static.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/01/09 6 | // 7 | // 8 | 9 | #if canImport(Darwin) 10 | extension DyldCacheLoaded { 11 | public static var current: DyldCacheLoaded? { 12 | var size = 0 13 | guard let ptr = _dyld_get_shared_cache_range(&size), 14 | let cache = try? DyldCacheLoaded(ptr: ptr) else { 15 | return nil 16 | } 17 | return cache 18 | } 19 | } 20 | #endif 21 | -------------------------------------------------------------------------------- /Sources/MachOKitC/include/dyld_cache.h: -------------------------------------------------------------------------------- 1 | // 2 | // dyld_cache.h 3 | // 4 | // 5 | // Created by p-x9 on 2024/10/09 6 | // 7 | // 8 | 9 | #ifndef dyld_cache_h 10 | #define dyld_cache_h 11 | 12 | #if defined(__APPLE__) 13 | 14 | #include 15 | #include 16 | 17 | extern const void* _dyld_get_shared_cache_range(size_t* length); 18 | extern const struct mach_header* dyld_image_header_containing_address(const void* addr); 19 | extern const char* dyld_shared_cache_file_path(void); 20 | 21 | #endif 22 | 23 | #endif /* dyld_cache_h */ 24 | -------------------------------------------------------------------------------- /Sources/MachOKit/LoadCommand/Model/Section+Flags.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Section+Flags.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/01. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct SectionFlags: Sendable { 12 | public let rawValue: UInt32 13 | 14 | public var type: SectionType? { 15 | let rawValue = rawValue & UInt32(SECTION_TYPE) 16 | return .init(rawValue: Int32(rawValue)) 17 | } 18 | 19 | public var attributes: SectionAttributes { 20 | .init(rawValue: rawValue & SECTION_ATTRIBUTES) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/MachOKit/FullDyldCache+host.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FullDyldCache+host.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/07/28 6 | // 7 | // 8 | 9 | import Foundation 10 | import MachOKitC 11 | 12 | extension FullDyldCache { 13 | public static var host: FullDyldCache? { 14 | #if canImport(Darwin) 15 | guard let ptr = dyld_shared_cache_file_path() else { 16 | return nil 17 | } 18 | let url: URL = .init(fileURLWithPath: .init(cString: ptr)) 19 | return try? FullDyldCache(url: url) 20 | #else 21 | return nil 22 | #endif 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Sources/MachOKit/LoadCommand/UUIDCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UUIDCommand.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/11/29. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct UUIDCommand: LoadCommandWrapper { 12 | public typealias Layout = uuid_command 13 | 14 | public var layout: Layout 15 | public var offset: Int // offset from mach header trailing 16 | 17 | public var uuid: UUID { 18 | .init(uuid: layout.uuid) 19 | } 20 | 21 | init(_ layout: Layout, offset: Int) { 22 | self.layout = layout 23 | self.offset = offset 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldCache/Loader/LoaderRef.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoaderRef.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/07/10 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct LoaderRef: LayoutWrapper, Sendable { 12 | public typealias Layout = loader_ref 13 | 14 | public var layout: Layout 15 | } 16 | 17 | extension LoaderRef { 18 | public var index: Int { 19 | numericCast(layout.index) 20 | } 21 | 22 | public var isApp: Bool { 23 | layout.app == 1 24 | } 25 | 26 | public var isDylib: Bool { 27 | layout.app == 0 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/MachOKit/Header/FatHeader/FatArch.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FatArch.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/04. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct FatArch: LayoutWrapper, Sendable { 12 | public var layout: fat_arch 13 | 14 | public var cpuType: CPUType? { 15 | cpu.type 16 | } 17 | 18 | public var cpuSubType: CPUSubType? { 19 | cpu.subtype 20 | } 21 | 22 | public var cpu: CPU { 23 | .init( 24 | typeRawValue: layout.cputype, 25 | subtypeRawValue: layout.cpusubtype 26 | ) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/Relocation/RelocationLength.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RelocationLength.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/01/10. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public enum RelocationLength: UInt32, Sendable { 12 | case byte 13 | case word 14 | case long 15 | case quad 16 | } 17 | 18 | extension RelocationLength: CustomStringConvertible { 19 | public var description: String { 20 | switch self { 21 | case .byte: "byte" 22 | case .word: "word" 23 | case .long: "long" 24 | case .quad: "quad" 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/MachOKit/Protocol/SymbolProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Symbol.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/14. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol SymbolProtocol { 12 | var name: String { get } 13 | 14 | /// Offset from start of mach header (`MachO`) 15 | /// File offset from mach header (`MachOFile`) 16 | var offset: Int { get } 17 | 18 | /// Nlist or Nlist64 19 | var nlist: any NlistProtocol { get } 20 | } 21 | 22 | extension SymbolProtocol { 23 | public var demangledName: String { 24 | stdlib_demangleName(name) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/MachOKit/Protocol/StringTable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StringTable.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/02/02 6 | // 7 | // 8 | 9 | public protocol StringTable: Sequence { 10 | associatedtype Encoding: _UnicodeEncoding 11 | 12 | /// Returns the string entry located at the specified offset within the string table. 13 | /// 14 | /// - Parameter offset: The byte offset from the start of the string table. 15 | /// - Returns: A `StringTableEntry` containing the string and its offset, 16 | /// or `nil` if the offset is out of bounds or invalid. 17 | func string(at offset: Int) -> Element? 18 | } 19 | -------------------------------------------------------------------------------- /Sources/MachOKit/Util/BitFlags.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BitFlags.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/01/20. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol BitFlags: OptionSet, Sendable { 12 | associatedtype Bit: CaseIterable, RawRepresentable, CustomStringConvertible where Bit.RawValue == RawValue 13 | 14 | associatedtype Element = Self 15 | 16 | var rawValue: RawValue { get } 17 | } 18 | 19 | extension BitFlags where Element == Self { 20 | public var bits: [Bit] { 21 | Bit.allCases 22 | .lazy 23 | .filter { 24 | contains(.init(rawValue: $0.rawValue)) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/MachOKit/LoadCommand/SourceVersionCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SourceVersionCommand.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/11/30. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct SourceVersionCommand: LoadCommandWrapper { 12 | public typealias Layout = source_version_command 13 | 14 | public var layout: Layout 15 | public var offset: Int // offset from mach header trailing 16 | 17 | init(_ layout: Layout, offset: Int) { 18 | self.layout = layout 19 | self.offset = offset 20 | } 21 | } 22 | 23 | extension SourceVersionCommand { 24 | public var version: SourceVersion { 25 | .init(layout.version) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldCache/DyldCacheFunctionVariantEntry.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldCacheFunctionVariantEntry.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/05/12 6 | // 7 | // 8 | 9 | import Foundation 10 | import MachOKitC 11 | 12 | public struct DyldCacheFunctionVariantEntry: LayoutWrapper, Sendable { 13 | public typealias Layout = dyld_cache_function_variant_entry 14 | 15 | public var layout: Layout 16 | } 17 | 18 | extension DyldCacheFunctionVariantEntry { 19 | public var isPACSigned: Bool { 20 | layout.pacAuth != 0 21 | } 22 | 23 | public var sizeOfFunctionVariantTable: Int { 24 | numericCast(layout.functionVariantTableSizeDiv4) * 4 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/MachOKit/MachOKit.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public enum File { 4 | case machO(MachOFile) 5 | case fat(FatFile) 6 | } 7 | 8 | public enum MachOKitError: LocalizedError { 9 | case invalidMagic 10 | case invalidCpuType 11 | } 12 | 13 | public func loadFromFile(url: URL) throws -> File { 14 | let fileHandle = try FileHandle(forReadingFrom: url) 15 | let magicRaw: UInt32 = fileHandle.read(offset: 0) 16 | 17 | guard let magic = Magic(rawValue: magicRaw) else { 18 | throw MachOKitError.invalidMagic 19 | } 20 | 21 | if magic.isFat { 22 | return .fat(try FatFile(url: url)) 23 | } else { 24 | return .machO(try MachOFile(url: url)) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/Codesign/CodeSignBlobIndex.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CodeSignBlobIndex.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/03/03. 6 | // 7 | // 8 | 9 | import Foundation 10 | import MachOKitC 11 | 12 | public struct CodeSignBlobIndex: LayoutWrapper, Sendable { 13 | public typealias Layout = CS_BlobIndex 14 | 15 | public var layout: Layout 16 | } 17 | 18 | extension CodeSignBlobIndex { 19 | public var type: CodeSignSlot! { 20 | .init(rawValue: layout.type) 21 | } 22 | } 23 | 24 | extension CS_BlobIndex { 25 | var swapped: CS_BlobIndex { 26 | .init( 27 | type: type.byteSwapped, 28 | offset: offset.byteSwapped 29 | ) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/MachOKit/Protocol/DyldChainedImportProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldChainedImportProtocol.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/01/11. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol DyldChainedImportProtocol: LayoutWrapper, Sendable { 12 | var libraryOrdinal: Int { get } 13 | var isWeakImport: Bool { get } 14 | var nameOffset: Int { get } 15 | var addend: Int { get } 16 | } 17 | 18 | extension DyldChainedImportProtocol { 19 | // https://opensource.apple.com/source/cctools/cctools-877.5/otool/dyld_bind_info.c.auto.html 20 | // `ordinalName` 21 | public var libraryOrdinalType: BindSpecial? { 22 | .init(rawValue: numericCast(libraryOrdinal)) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Sources/MachOKit/LoadCommand/VersionMinCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VersionMinCommand.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/11/29. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct VersionMinCommand: LoadCommandWrapper { 12 | public typealias Layout = version_min_command 13 | 14 | public var layout: version_min_command 15 | 16 | public var offset: Int 17 | 18 | init(_ layout: Layout, offset: Int) { 19 | self.layout = layout 20 | self.offset = offset 21 | } 22 | } 23 | 24 | extension VersionMinCommand { 25 | public var version: Version { 26 | .init(layout.version) 27 | } 28 | 29 | public var sdk: Version { 30 | .init(layout.version) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Sources/MachOKit/Protocol/DyldCacheLocalSymbolsEntryProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldCacheLocalSymbolsEntryProtocol.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/01/20. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol DyldCacheLocalSymbolsEntryProtocol: Sendable { 12 | /// Offset in cache buffer of start of dylib 13 | var dylibOffset: Int { get } 14 | 15 | /// Start index of locals for this dylib 16 | var nlistStartIndex: Int { get } 17 | 18 | /// Number of local symbols for this dylib 19 | var nlistCount: Int { get } 20 | } 21 | 22 | extension DyldCacheLocalSymbolsEntryProtocol { 23 | public var nlistRange: Range { 24 | nlistStartIndex ..< nlistStartIndex + nlistCount 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DependedDylib.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DependedDylib.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/02/03. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct DependedDylib: Sendable { 12 | public enum DependType: Sendable { 13 | case load 14 | case weakLoad 15 | case reexport 16 | case upwardLoad 17 | case lazyLoad 18 | } 19 | 20 | public let dylib: Dylib 21 | public let type: DependType 22 | public let useFlags: DylibUseFlags 23 | 24 | init( 25 | dylib: Dylib, 26 | type: DependType, 27 | useFlags: DylibUseFlags = [] 28 | ) { 29 | self.dylib = dylib 30 | self.type = type 31 | self.useFlags = useFlags 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldChain/DyldChainedStartsInImage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldChainedStartsInImage.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/01/11. 6 | // 7 | // 8 | 9 | import Foundation 10 | import MachOKitC 11 | 12 | public struct DyldChainedStartsInImage: LayoutWrapper, Sendable { 13 | public typealias Layout = dyld_chained_starts_in_image 14 | 15 | public var layout: Layout 16 | public let offset: Int 17 | } 18 | 19 | extension DyldChainedStartsInImage { 20 | public var swapped: Self { 21 | var layout = self.layout 22 | layout.seg_count = layout.seg_count.byteSwapped 23 | layout.seg_info_offset = layout.seg_info_offset.byteSwapped 24 | return .init(layout: layout, offset: offset) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/AotCache/BranchData/AotBranchDataIndexEntry.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AotBranchDataIndexEntry.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/12/18 6 | // 7 | // 8 | 9 | import MachOKitC 10 | 11 | public struct AotBranchDataIndexEntry: LayoutWrapper { 12 | public typealias Layout = aot_branch_data_index_entry 13 | 14 | public var layout: Layout 15 | } 16 | 17 | public struct AotBranchDataIndexEntryCompact: LayoutWrapper { 18 | public typealias Layout = aot_branch_data_index_entry_compact 19 | 20 | public var layout: Layout 21 | } 22 | 23 | public struct AotBranchDataIndexEntryExtended: LayoutWrapper { 24 | public typealias Layout = aot_branch_data_index_entry_extended 25 | 26 | public var layout: Layout 27 | } 28 | -------------------------------------------------------------------------------- /Sources/MachOKit/LoadCommand/EntryPointCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EntryPointCommand.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/01. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct EntryPointCommand: LoadCommandWrapper { 12 | public typealias Layout = entry_point_command 13 | 14 | public var layout: Layout 15 | public var offset: Int // offset from mach header trailing 16 | 17 | init(_ layout: Layout, offset: Int) { 18 | self.layout = layout 19 | self.offset = offset 20 | } 21 | } 22 | 23 | extension EntryPointCommand { 24 | public func mainStartPointer(machOStart: UnsafeRawPointer) -> UnsafeRawPointer { 25 | machOStart 26 | .advanced(by: numericCast(layout.entryoff)) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/MachOKit/Header/AotCacheHeader/AotCacheHeader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AotCacheHeader.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/01/29 6 | // 7 | // 8 | 9 | import Foundation 10 | import MachOKitC 11 | 12 | public struct AotCacheHeader: LayoutWrapper { 13 | public typealias Layout = aot_cache_header 14 | 15 | public var layout: Layout 16 | } 17 | 18 | extension AotCacheHeader { 19 | public var magic: String { 20 | .init(tuple: layout.magic) 21 | } 22 | 23 | public var uuid: UUID { 24 | .init(uuid: layout.uuid) 25 | } 26 | 27 | public var x86UUID: UUID { 28 | .init(uuid: layout.x86_uuid) 29 | } 30 | 31 | public var headerSize: Int { 32 | numericCast(layout.header_size) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldChain/DyldChainedImportFormat.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldChainedImportFormat.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/01/11. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public enum DyldChainedImportFormat: UInt32, Sendable { 12 | /// DYLD_CHAINED_IMPORT 13 | case general = 1 14 | /// DYLD_CHAINED_IMPORT_ADDEND 15 | case addend 16 | /// DYLD_CHAINED_IMPORT_ADDEND64 17 | case addend64 18 | } 19 | 20 | extension DyldChainedImportFormat: CustomStringConvertible { 21 | public var description: String { 22 | switch self { 23 | case .general: "DYLD_CHAINED_IMPORT" 24 | case .addend: "DYLD_CHAINED_IMPORT_ADDEND" 25 | case .addend64: "DYLD_CHAINED_IMPORT_ADDEND64" 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldChain/DyldChainedStartsOffsets.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldChainedStartsOffsets.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/01/11. 6 | // 7 | // 8 | 9 | import Foundation 10 | import MachOKitC 11 | 12 | public struct DyldChainedStartsOffsets: LayoutWrapper, Sendable { 13 | public typealias Layout = dyld_chained_starts_offsets 14 | 15 | public var layout: Layout 16 | } 17 | 18 | extension DyldChainedStartsOffsets { 19 | public var swapped: Self { 20 | var layout = self.layout 21 | layout.pointer_format = layout.pointer_format.byteSwapped 22 | layout.starts_count = layout.starts_count.byteSwapped 23 | layout.chain_starts = layout.chain_starts.byteSwapped 24 | return .init(layout: layout) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldCache/SwiftOptimization.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftOptimization.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/07/05 6 | // 7 | // 8 | 9 | import Foundation 10 | import MachOKitC 11 | 12 | public struct SwiftOptimization: LayoutWrapper, Sendable { 13 | public typealias Layout = swift_optimization 14 | 15 | public var layout: Layout 16 | } 17 | 18 | extension SwiftOptimization { 19 | public func hasProperty(_ keyPath: KeyPath) -> Bool { 20 | switch keyPath { 21 | case \.prespecializationDataCacheOffset: 22 | return layout.version >= 2 23 | case \.prespecializedMetadataHashTableCacheOffsets: 24 | return layout.version >= 3 25 | default: 26 | return true 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/MachOKit/LoadCommand/Model/Dylib.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Dylib.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/11/29. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct Dylib: Sendable { 12 | /// library's path name 13 | public var name: String 14 | 15 | /// library's build time stamp 16 | public var timestamp: Date 17 | 18 | /// library's current version number 19 | public var currentVersion: Version 20 | 21 | /// library's compatibility vers number 22 | public var compatibilityVersion: Version 23 | } 24 | 25 | extension Dylib { 26 | /// A boolean value that indicates whether self is loaded from `dylib_use_command` 27 | public var isFromDylibUseCommand: Bool { 28 | timestamp.timeIntervalSince1970 == TimeInterval(DYLIB_USE_MARKER) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/Bind/BindOperationsKind.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BindOperationsKind.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/09. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public enum BindOperationsKind: Sendable { 12 | case normal 13 | case weak 14 | case lazy 15 | } 16 | 17 | extension BindOperationsKind { 18 | func bindOffset(of info: dyld_info_command) -> UInt32 { 19 | switch self { 20 | case .normal: info.bind_off 21 | case .weak: info.weak_bind_off 22 | case .lazy: info.lazy_bind_off 23 | } 24 | } 25 | 26 | func bindSize(of info: dyld_info_command) -> UInt32 { 27 | switch self { 28 | case .normal: info.bind_size 29 | case .weak: info.weak_bind_size 30 | case .lazy: info.lazy_bind_size 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldCache/DylibsTrieNodeContent.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DylibsTrieNodeContent.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/07/07 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public typealias DylibsTrieEntry = TrieNode 12 | 13 | public struct DylibsTrieNodeContent: Sendable { 14 | public let index: UInt32 15 | } 16 | 17 | extension DylibsTrieNodeContent: TrieNodeContent { 18 | public static func read( 19 | basePointer: UnsafePointer, 20 | trieSize _: Int, 21 | nextOffset: inout Int 22 | ) -> DylibsTrieNodeContent? { 23 | let (index, ulebOffset) = basePointer 24 | .advanced(by: nextOffset) 25 | .readULEB128() 26 | 27 | nextOffset += ulebOffset 28 | 29 | return .init(index: numericCast(index)) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/Relocation/ScatteredRelocationInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScatteredRelocationInfo.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/01/10. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct ScatteredRelocationInfo: LayoutWrapper, Sendable { 12 | public typealias Layout = scattered_relocation_info 13 | 14 | public var layout: Layout 15 | } 16 | 17 | extension ScatteredRelocationInfo { 18 | public var isRelocatedPCRelative: Bool { 19 | layout.r_pcrel != 0 20 | } 21 | 22 | public var length: RelocationLength? { 23 | .init(rawValue: layout.r_length) 24 | } 25 | 26 | public var isScattered: Bool { 27 | layout.r_scattered != 0 28 | } 29 | 30 | public func type(for cpuType: CPUType) -> RelocationType? { 31 | .init(rawValue: layout.r_type, for: cpuType) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldCache/ProgramsTrieNodeContent.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProgramsTrieNodeContent.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/07/09 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public typealias ProgramsTrieEntry = TrieNode 12 | 13 | public struct ProgramsTrieNodeContent: Sendable { 14 | public let offset: UInt32 15 | } 16 | 17 | extension ProgramsTrieNodeContent: TrieNodeContent { 18 | public static func read( 19 | basePointer: UnsafePointer, 20 | trieSize _: Int, 21 | nextOffset: inout Int 22 | ) -> ProgramsTrieNodeContent? { 23 | let (offset, ulebOffset) = basePointer 24 | .advanced(by: nextOffset) 25 | .readULEB128() 26 | 27 | nextOffset += ulebOffset 28 | 29 | return .init(offset: numericCast(offset)) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/MachOKit/Extension/Data+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Data+.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/02/02 6 | // 7 | // 8 | import Foundation 9 | 10 | extension Data { 11 | func byteSwapped(_ type: T.Type) -> Data { 12 | guard count >= MemoryLayout.size else { return self } 13 | 14 | let valueArray = self.withUnsafeBytes { 15 | Array($0.bindMemory(to: T.self)) 16 | } 17 | 18 | let swappedArray = valueArray.map { $0.byteSwapped } 19 | 20 | var swappedData = swappedArray.withUnsafeBufferPointer { 21 | Data(buffer: $0) 22 | } 23 | 24 | let remainingBytes = count % MemoryLayout.size 25 | if remainingBytes > 0 { 26 | swappedData.append(self.suffix(remainingBytes)) 27 | } 28 | 29 | return swappedData 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.github/workflows/docc.yml: -------------------------------------------------------------------------------- 1 | name: DocC 2 | 3 | on: 4 | workflow_call: 5 | workflow_dispatch: 6 | 7 | jobs: 8 | build: 9 | name: Generate DocC 10 | runs-on: macos-15 11 | steps: 12 | - uses: actions/checkout@v3 13 | 14 | - name: Select Xcode 16.4 15 | run: sudo xcode-select -s /Applications/Xcode_16.4.app 16 | 17 | - name: Build DocC 18 | run: | 19 | make docc 20 | 21 | - uses: actions/upload-pages-artifact@v3 22 | with: 23 | path: docs 24 | 25 | deploy: 26 | needs: build 27 | permissions: 28 | pages: write 29 | id-token: write 30 | environment: 31 | name: github-pages 32 | url: ${{ steps.deployment.outputs.page_url }} 33 | runs-on: macos-15 34 | steps: 35 | - name: Deploy to GitHub Pages 36 | id: deployment 37 | uses: actions/deploy-pages@v4 38 | -------------------------------------------------------------------------------- /Sources/MachOKitC/include/swift.h: -------------------------------------------------------------------------------- 1 | // 2 | // swift.h 3 | // 4 | // 5 | // Created by p-x9 on 2024/07/05 6 | // 7 | // 8 | 9 | #ifndef swift_h 10 | #define swift_h 11 | 12 | #include 13 | 14 | // ref: https://github.com/apple-oss-distributions/dyld/blob/031f1c6ffb240a094f3f2f85f20dfd9e3f15b664/common/OptimizerSwift.h#L45 15 | struct swift_optimization { 16 | uint32_t version; 17 | uint32_t padding; 18 | uint64_t typeConformanceHashTableCacheOffset; 19 | uint64_t metadataConformanceHashTableCacheOffset; 20 | uint64_t foreignTypeConformanceHashTableCacheOffset; 21 | 22 | uint64_t prespecializationDataCacheOffset; // added in version 2 23 | 24 | // limited space reserved for table offsets, they're not accessed directly 25 | // used for debugging only 26 | uint64_t prespecializedMetadataHashTableCacheOffsets[8]; // added in version 3 27 | }; 28 | 29 | #endif /* swift_h */ 30 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/FunctionStart.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FunctionStart.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/01/07. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct FunctionStart: Sendable { 12 | /// Offset from start of mach header (`MachO`) 13 | /// File offset from mach header (`MachOFile`) 14 | public let offset: UInt 15 | } 16 | 17 | extension FunctionStart { 18 | internal static func readNext( 19 | basePointer: UnsafePointer, 20 | functionStartsSize: Int, 21 | lastFunctionOffset: UInt, 22 | nextOffset: inout Int 23 | ) -> FunctionStart? { 24 | guard nextOffset < functionStartsSize else { return nil } 25 | 26 | let (additionalOffset, size) = basePointer 27 | .advanced(by: nextOffset) 28 | .readULEB128() 29 | nextOffset += size 30 | 31 | return .init(offset: lastFunctionOffset + additionalOffset) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/MachOKitC/include/core_foundation.h: -------------------------------------------------------------------------------- 1 | // 2 | // core_foundation.h 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/02/01 6 | // 7 | // 8 | 9 | #ifndef core_foundation_h 10 | #define core_foundation_h 11 | 12 | // ref: https://github.com/apple-oss-distributions/CF/blob/dc54c6bb1c1e5e0b9486c1d26dd5bef110b20bf3/CFRuntime.h#L222-L228 13 | typedef struct __CFRuntimeBase64 { 14 | uint64_t _cfisa; 15 | uint8_t _cfinfo[4]; 16 | uint32_t _rc; 17 | } CFRuntimeBase64; 18 | 19 | typedef struct __CFRuntimeBase32 { 20 | uint32_t _cfisa; 21 | uint8_t _cfinfo[4]; 22 | } CFRuntimeBase32; 23 | 24 | // ref: https://github.com/apple-oss-distributions/CF/blob/dc54c6bb1c1e5e0b9486c1d26dd5bef110b20bf3/CFInternal.h#L332 25 | struct CF_CONST_STRING64 { 26 | CFRuntimeBase64 _base; 27 | uint64_t _ptr; 28 | uint32_t _length; 29 | }; 30 | 31 | struct CF_CONST_STRING32 { 32 | CFRuntimeBase32 _base; 33 | uint32_t _ptr; 34 | uint32_t _length; 35 | }; 36 | 37 | #endif /* core_foundation_h */ 38 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldCache/DyldCacheMappingInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldCacheMappingInfo.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/01/15. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct DyldCacheMappingInfo: LayoutWrapper, Sendable { 12 | public typealias Layout = dyld_cache_mapping_info 13 | 14 | public var layout: Layout 15 | } 16 | 17 | extension DyldCacheMappingInfo { 18 | /// Max vm protection of this mapping 19 | public var maxProtection: VMProtection { 20 | .init(rawValue: VMProtection.RawValue(bitPattern: layout.maxProt)) 21 | } 22 | 23 | /// Initial vm protection of this mapping 24 | public var initialProtection: VMProtection { 25 | .init(rawValue: VMProtection.RawValue(bitPattern: layout.maxProt)) 26 | } 27 | } 28 | 29 | extension DyldCacheMappingInfo { 30 | internal func withFileOffset(_ value: UInt64) -> Self { 31 | var layout = layout 32 | layout.fileOffset = value 33 | return .init(layout: layout) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/AotCache/InstructionMap/AotInstructionMap.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AotInstructionMap.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/12/17 6 | // 7 | // 8 | 9 | import Foundation 10 | import MachOKitC 11 | 12 | public struct AotInstructionMap { 13 | public let header: AotInstructionMapHeader 14 | public let offset: Int 15 | } 16 | 17 | extension AotInstructionMap { 18 | public func entries(in cache: AotCache) -> DataSequence { 19 | cache.fileHandle.readDataSequence( 20 | offset: UInt64(offset + MemoryLayout.size), 21 | numberOfElements: numericCast(header.entry_count) 22 | ) 23 | } 24 | 25 | public func entries(in machO: MachOFile) -> DataSequence { 26 | machO.fileHandle.readDataSequence( 27 | offset: UInt64(offset + MemoryLayout.size), 28 | numberOfElements: numericCast(header.entry_count) 29 | ) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/MachOKit/LoadCommand/Model/ThreadState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ThreadState.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/01/13 6 | // 7 | // 8 | 9 | import Foundation 10 | import MachOKitC 11 | 12 | public enum ThreadState: Sendable { 13 | case arm(ARMThreadState) 14 | case arm64(ARM64ThreadState) 15 | case i386(i386ThreadState) 16 | case x86_64(x86_64ThreadState) 17 | } 18 | 19 | public struct x86_64ThreadState: LayoutWrapper, Sendable { 20 | public typealias Layout = x86_thread_state64 21 | 22 | public var layout: Layout 23 | } 24 | 25 | public struct i386ThreadState: LayoutWrapper, Sendable { 26 | public typealias Layout = i386_thread_state 27 | 28 | public var layout: Layout 29 | } 30 | 31 | public struct ARMThreadState: LayoutWrapper, Sendable { 32 | public typealias Layout = arm_thread_state 33 | 34 | public var layout: Layout 35 | } 36 | 37 | public struct ARM64ThreadState: LayoutWrapper, Sendable { 38 | public typealias Layout = arm_thread_state64 39 | 40 | public var layout: Layout 41 | } 42 | -------------------------------------------------------------------------------- /Sources/MachOKit/Protocol/DyldChainedFixupsProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldChainedFixupsProtocol.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/01/11. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol DyldChainedFixupsProtocol { 12 | var header: DyldChainedFixupsHeader? { get } 13 | var startsInImage: DyldChainedStartsInImage? { get } 14 | var imports: [DyldChainedImport] { get } 15 | 16 | func startsInSegments( 17 | of startsInImage: DyldChainedStartsInImage? 18 | ) -> [DyldChainedStartsInSegment] 19 | 20 | func pages( 21 | of startsInSegment: DyldChainedStartsInSegment? 22 | ) -> [DyldChainedPage] 23 | 24 | func symbolName(for nameOffset: Int) -> String? 25 | func demangledSymbolName(for nameOffset: Int) -> String? 26 | } 27 | 28 | extension DyldChainedFixupsProtocol { 29 | public func demangledSymbolName(for nameOffset: Int) -> String? { 30 | guard let symbolName = symbolName(for: nameOffset) else { 31 | return nil 32 | } 33 | return stdlib_demangleName(symbolName) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldCache/SlideInfo/DyldCacheSlideInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldCacheSlideInfo.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/07/23 6 | // 7 | // 8 | 9 | import Foundation 10 | import MachOKitC 11 | 12 | public enum DyldCacheSlideInfo: Sendable { 13 | case v1(DyldCacheSlideInfo1) 14 | case v2(DyldCacheSlideInfo2) 15 | case v3(DyldCacheSlideInfo3) 16 | case v4(DyldCacheSlideInfo4) 17 | case v5(DyldCacheSlideInfo5) 18 | } 19 | 20 | extension DyldCacheSlideInfo { 21 | public enum Version: Int, Sendable { 22 | case none 23 | case v1 = 1, v2, v3, v4, v5 24 | } 25 | } 26 | 27 | extension DyldCacheSlideInfo.Version: Comparable { 28 | public static func < (lhs: DyldCacheSlideInfo.Version, rhs: DyldCacheSlideInfo.Version) -> Bool { 29 | lhs.rawValue < rhs.rawValue 30 | } 31 | } 32 | 33 | extension DyldCacheSlideInfo { 34 | public var version: Version { 35 | switch self { 36 | case .v1: .v1 37 | case .v2: .v2 38 | case .v3: .v3 39 | case .v4: .v4 40 | case .v5: .v5 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 p-x9 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/ExportedSymbol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExportedSymbol.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/11. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct ExportedSymbol: Sendable { 12 | public var name: String 13 | /// Symbol offset from start of mach header (`MachO`) 14 | /// Symbol offset from start of file (`MachOFile`) 15 | public var offset: Int? 16 | 17 | public var flags: ExportSymbolFlags 18 | 19 | public var ordinal: UInt? 20 | public var importedName: String? 21 | 22 | public var stub: UInt? 23 | public var resolverOffset: UInt? 24 | 25 | public var functionVariantTableIndex: UInt? 26 | } 27 | 28 | extension ExportedSymbol { 29 | // [dyld implementation](https://github.com/apple-oss-distributions/dyld/blob/66c652a1f1f6b7b5266b8bbfd51cb0965d67cc44/common/MachOLoaded.cpp#L258) 30 | public func resolver(for machO: MachOImage) -> (@convention(c) () -> UInt)? { 31 | guard let resolverOffset else { return nil } 32 | return autoBitCast( 33 | machO.ptr.advanced(by: numericCast(resolverOffset)) 34 | ) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldCache/DyldCacheDynamicData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldCacheDynamicData.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/02/28 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct DyldCacheDynamicData: LayoutWrapper, Sendable { 12 | public typealias Layout = dyld_cache_dynamic_data_header 13 | 14 | public var layout: Layout 15 | } 16 | 17 | extension DyldCacheDynamicData { 18 | public var magic: String { 19 | .init(tuple: layout.magic) 20 | } 21 | } 22 | 23 | #if canImport(Darwin) 24 | extension DyldCacheDynamicData { 25 | public var path: String { 26 | let path: UnsafeMutablePointer = .allocate(capacity: Int(MAXPATHLEN)) 27 | var fsid: UInt64 = numericCast(layout.fsId) 28 | return withUnsafeMutablePointer(to: &fsid) { fsid in 29 | fsgetpath( 30 | path, 31 | Int(MAXPATHLEN), 32 | UnsafeMutableRawPointer(fsid).assumingMemoryBound(to: fsid_t.self), 33 | layout.fsObjId 34 | ) 35 | return String(cString: path) 36 | } 37 | } 38 | } 39 | #endif 40 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths-ignore: 8 | - README.md 9 | - LICENSE 10 | pull_request: 11 | paths-ignore: 12 | - README.md 13 | - LICENSE 14 | workflow_dispatch: 15 | 16 | permissions: 17 | contents: read 18 | 19 | env: 20 | DEVELOPER_DIR: /Applications/Xcode_16.2.app 21 | 22 | jobs: 23 | build: 24 | name: Build & Test 25 | runs-on: macos-15 26 | steps: 27 | - name: Checkout 28 | uses: actions/checkout@v4 29 | 30 | - name: Select Xcode 16 31 | run: sudo xcode-select -s /Applications/Xcode_16.2.app 32 | 33 | - name: Build 34 | run: swift build 35 | 36 | linux-build: 37 | name: Linux Test 38 | runs-on: ubuntu-22.04 39 | steps: 40 | - name: Install Swift 41 | # WORKAROUND:https://github.com/swift-actions/setup-swift/pull/680 42 | uses: swift-actions/setup-swift@bb83339d1e8577741bdc6c65ba551ce7dc0fb854 43 | with: 44 | swift-version: '5.10.1' 45 | 46 | - uses: actions/checkout@v4 47 | 48 | - name: Build 49 | run: swift build 50 | -------------------------------------------------------------------------------- /Sources/MachOKit/Header/MachHeader/MachHeader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MachHeader.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/11/29. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct MachHeader: LayoutWrapper, Sendable { 12 | public var layout: mach_header 13 | 14 | public var magic: Magic! { 15 | .init(rawValue: layout.magic) 16 | } 17 | 18 | public var cpuType: CPUType? { 19 | cpu.type 20 | } 21 | 22 | public var cpuSubType: CPUSubType? { 23 | cpu.subtype 24 | } 25 | 26 | public var cpu: CPU { 27 | .init( 28 | typeRawValue: layout.cputype, 29 | subtypeRawValue: layout.cpusubtype 30 | ) 31 | } 32 | 33 | public var fileType: FileType? { 34 | .init(rawValue: numericCast(layout.filetype)) 35 | } 36 | 37 | public var flags: Flags { 38 | .init(rawValue: numericCast(layout.flags)) 39 | } 40 | } 41 | 42 | extension MachHeader { 43 | /// A boolean value that indicates whether this MachO exists in the dyld cache or not. 44 | public var isInDyldCache: Bool { 45 | flags.contains(.dylib_in_cache) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Sources/MachOKit/LoadCommand/NoteCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NoteCommand.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2024/12/11 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct NoteCommand: LoadCommandWrapper { 12 | public typealias Layout = note_command 13 | 14 | public var layout: Layout 15 | public var offset: Int // offset from mach header trailing 16 | 17 | init(_ layout: Layout, offset: Int) { 18 | self.layout = layout 19 | self.offset = offset 20 | } 21 | } 22 | 23 | extension NoteCommand { 24 | public var dataOwner: String { 25 | .init(tuple: layout.data_owner) 26 | } 27 | 28 | public func data(in machO: MachOFile) -> Data { 29 | try! machO.fileHandle.readData( 30 | offset: machO.headerStartOffset + numericCast(layout.offset), 31 | length: numericCast(layout.size) 32 | ) 33 | } 34 | 35 | public func data(in machO: MachOImage) -> Data { 36 | .init( 37 | bytes: machO.ptr 38 | .advanced(by: numericCast(layout.offset)), 39 | count: numericCast(layout.size) 40 | ) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/Relocation/RelocationInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RelocationInfo.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/01/10. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct RelocationInfo: LayoutWrapper, Sendable { 12 | public typealias Layout = relocation_info 13 | 14 | public var layout: Layout 15 | } 16 | 17 | extension RelocationInfo { 18 | public var isRelocatedPCRelative: Bool { 19 | layout.r_pcrel != 0 20 | } 21 | 22 | public var length: RelocationLength? { 23 | .init(rawValue: layout.r_length) 24 | } 25 | 26 | public var isExternal: Bool { 27 | layout.r_extern != 0 28 | } 29 | 30 | public var isScattered: Bool { 31 | UInt32(bitPattern: layout.r_address) & R_SCATTERED != 0 32 | } 33 | 34 | public var symbolIndex: Int? { 35 | isExternal ? numericCast(layout.r_symbolnum) : nil 36 | } 37 | 38 | public var sectionOrdinal: Int? { 39 | isExternal ? nil : numericCast(layout.r_symbolnum) 40 | } 41 | 42 | public func type(for cpuType: CPUType) -> RelocationType? { 43 | .init(rawValue: layout.r_type, for: cpuType) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldCache/ObjCOptimization/ObjCHeaderInfoRW.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ObjCHeaderInfoRW.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/10/14 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol ObjCHeaderInfoRWProtocol: Sendable { 12 | /// A boolean value that indicates whether objc image is already loaded or not 13 | var isLoaded: Bool { get } 14 | /// A boolean value that indicates whether all objc classes contained in objc image are realized 15 | var isAllClassesRelized: Bool { get } 16 | } 17 | 18 | public struct ObjCHeaderInfoRW64: LayoutWrapper, ObjCHeaderInfoRWProtocol { 19 | public typealias Layout = header_info_rw_64 20 | 21 | public var layout: Layout 22 | 23 | public var isLoaded: Bool { layout.isLoaded == 1 } 24 | public var isAllClassesRelized: Bool { layout.allClassesRealized == 1 } 25 | } 26 | 27 | public struct ObjCHeaderInfoRW32: LayoutWrapper, ObjCHeaderInfoRWProtocol { 28 | public typealias Layout = header_info_rw_32 29 | 30 | public var layout: Layout 31 | 32 | public var isLoaded: Bool { layout.isLoaded == 1 } 33 | public var isAllClassesRelized: Bool { layout.allClassesRealized == 1 } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/IndirectSymbol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IndirectSymbol.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/26. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct IndirectSymbol: Sendable { 12 | let _value: UInt32 13 | } 14 | 15 | extension IndirectSymbol { 16 | /// index of symbols 17 | public var index: Int? { 18 | guard !isLocal, !isAbsolute else { return nil } 19 | return numericCast(_value) 20 | } 21 | 22 | /// INDIRECT_SYMBOL_LOCAL 23 | public var isLocal: Bool { 24 | _value & ~UInt32(INDIRECT_SYMBOL_ABS) == INDIRECT_SYMBOL_LOCAL 25 | } 26 | 27 | /// INDIRECT_SYMBOL_ABS 28 | public var isAbsolute: Bool { 29 | _value & UInt32(INDIRECT_SYMBOL_ABS) != 0 30 | } 31 | } 32 | 33 | extension IndirectSymbol: CustomStringConvertible { 34 | public var description: String { 35 | if isLocal && isAbsolute { 36 | "INDIRECT_SYMBOL_LOCAL & INDIRECT_SYMBOL_ABS" 37 | } else if isLocal { 38 | "INDIRECT_SYMBOL_LOCAL" 39 | } else if isAbsolute { 40 | "INDIRECT_SYMBOL_ABS" 41 | } else { 42 | "\(_value)" 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/MachOKit/LoadCommand/RpathCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RpathCommand.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/11/29. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct RpathCommand: LoadCommandWrapper { 12 | public typealias Layout = rpath_command 13 | 14 | public var layout: Layout 15 | public var offset: Int // offset from mach header trailing 16 | 17 | init(_ layout: Layout, offset: Int) { 18 | self.layout = layout 19 | self.offset = offset 20 | } 21 | } 22 | 23 | extension RpathCommand { 24 | public func path(cmdsStart: UnsafeRawPointer) -> String { 25 | let ptr = cmdsStart 26 | .advanced(by: offset) 27 | .advanced(by: Int(layout.path.offset)) 28 | .assumingMemoryBound(to: CChar.self) 29 | return String(cString: ptr) 30 | } 31 | } 32 | 33 | extension RpathCommand { 34 | public func path(in machO: MachOFile) -> String { 35 | let offset = machO.cmdsStartOffset + offset + Int(layout.path.offset) 36 | return machO.fileHandle.readString( 37 | offset: numericCast(offset), 38 | size: Int(layout.cmdsize) - layoutSize 39 | ) ?? "" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/Relocation/Relocation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Relocation.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/01/10. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct Relocation: Sendable { 12 | public enum Info: Sendable { 13 | case general(RelocationInfo) 14 | case scattered(ScatteredRelocationInfo) 15 | } 16 | 17 | public let _data: UInt64 18 | 19 | public var isScattered: Bool { 20 | _data & UInt64(R_SCATTERED) != 0 21 | } 22 | 23 | public var info: Info { 24 | var buffer = _data 25 | if isScattered { 26 | let info: ScatteredRelocationInfo = withUnsafePointer( 27 | to: &buffer, { 28 | let ptr = UnsafeRawPointer($0) 29 | return ptr.autoBoundPointee() 30 | } 31 | ) 32 | return .scattered(info) 33 | } else { 34 | let info: RelocationInfo = withUnsafePointer( 35 | to: &buffer, { 36 | let ptr = UnsafeRawPointer($0) 37 | return ptr.autoBoundPointee() 38 | } 39 | ) 40 | return .general(info) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/MachOKit/Protocol/LayoutWrapper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LayoutWrapper.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/11/29. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | @dynamicMemberLookup 12 | public protocol LayoutWrapper { 13 | associatedtype Layout 14 | 15 | var layout: Layout { get set } 16 | } 17 | 18 | extension LayoutWrapper { 19 | public subscript(dynamicMember keyPath: KeyPath) -> Value { 20 | layout[keyPath: keyPath] 21 | } 22 | } 23 | 24 | extension LayoutWrapper { 25 | @_spi(Support) 26 | public static var layoutSize: Int { 27 | MemoryLayout.size 28 | } 29 | 30 | @_spi(Support) 31 | public var layoutSize: Int { 32 | MemoryLayout.size 33 | } 34 | } 35 | 36 | extension LayoutWrapper { 37 | @_spi(Support) 38 | public static func layoutOffset(of key: PartialKeyPath) -> Int { 39 | MemoryLayout.offset(of: key)! // swiftlint:disable:this force_unwrapping 40 | } 41 | 42 | @_spi(Support) 43 | public func layoutOffset(of key: PartialKeyPath) -> Int { 44 | MemoryLayout.offset(of: key)! // swiftlint:disable:this force_unwrapping 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/MachOKit/LoadCommand/DylinkerCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DylinkerCommand.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/02. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct DylinkerCommand: LoadCommandWrapper { 12 | public typealias Layout = dylinker_command 13 | 14 | public var layout: Layout 15 | public var offset: Int // offset from mach header trailing 16 | 17 | init(_ layout: Layout, offset: Int) { 18 | self.layout = layout 19 | self.offset = offset 20 | } 21 | } 22 | 23 | extension DylinkerCommand { 24 | public func name(cmdsStart: UnsafeRawPointer) -> String { 25 | let ptr = cmdsStart 26 | .advanced(by: offset) 27 | .advanced(by: Int(layout.name.offset)) 28 | .assumingMemoryBound(to: CChar.self) 29 | return String(cString: ptr) 30 | } 31 | } 32 | 33 | extension DylinkerCommand { 34 | public func name(in machO: MachOFile) -> String { 35 | let offset = machO.cmdsStartOffset + offset + Int(layout.name.offset) 36 | return machO.fileHandle.readString( 37 | offset: numericCast(offset), 38 | size: Int(layout.cmdsize) - layoutSize 39 | ) ?? "" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Sources/MachOKit/LoadCommand/SubFrameworkCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SubFrameworkCommand.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/12/13 6 | // 7 | // 8 | 9 | public struct SubFrameworkCommand: LoadCommandWrapper { 10 | public typealias Layout = sub_framework_command 11 | 12 | public var layout: Layout 13 | public var offset: Int // offset from mach header trailing 14 | 15 | init(_ layout: Layout, offset: Int) { 16 | self.layout = layout 17 | self.offset = offset 18 | } 19 | } 20 | 21 | extension SubFrameworkCommand { 22 | public func umbrella(cmdsStart: UnsafeRawPointer) -> String { 23 | let ptr = cmdsStart 24 | .advanced(by: offset) 25 | .advanced(by: Int(layout.umbrella.offset)) 26 | .assumingMemoryBound(to: CChar.self) 27 | return String(cString: ptr) 28 | } 29 | } 30 | 31 | extension SubFrameworkCommand { 32 | public func umbrella(in machO: MachOFile) -> String { 33 | let offset = machO.cmdsStartOffset + offset + Int(layout.umbrella.offset) 34 | return machO.fileHandle.readString( 35 | offset: numericCast(offset), 36 | size: Int(layout.cmdsize) - layoutSize 37 | ) ?? "" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/MachOKit/LoadCommand/TargetTripleCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TargetTripleCommand.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/02/22 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct TargetTripleCommand: LoadCommandWrapper { 12 | public typealias Layout = target_triple_command 13 | 14 | public var layout: Layout 15 | public var offset: Int // offset from mach header trailing 16 | 17 | init(_ layout: Layout, offset: Int) { 18 | self.layout = layout 19 | self.offset = offset 20 | } 21 | } 22 | 23 | extension TargetTripleCommand { 24 | public func triple(cmdsStart: UnsafeRawPointer) -> String { 25 | let ptr = cmdsStart 26 | .advanced(by: offset) 27 | .advanced(by: Int(layout.triple.offset)) 28 | .assumingMemoryBound(to: CChar.self) 29 | return String(cString: ptr) 30 | } 31 | } 32 | 33 | extension TargetTripleCommand { 34 | public func path(in machO: MachOFile) -> String { 35 | let offset = machO.cmdsStartOffset + offset + Int(layout.triple.offset) 36 | return machO.fileHandle.readString( 37 | offset: numericCast(offset), 38 | size: Int(layout.cmdsize) - layoutSize 39 | ) ?? "" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldChain/DyldChainedStartsInSegment.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldChainedStartsInSegment.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/01/11. 6 | // 7 | // 8 | 9 | import Foundation 10 | import MachOKitC 11 | 12 | public struct DyldChainedStartsInSegment: LayoutWrapper, Sendable { 13 | public typealias Layout = dyld_chained_starts_in_segment 14 | 15 | public var layout: Layout 16 | public let offset: Int 17 | public let segmentIndex: Int 18 | 19 | public var pointerFormat: DyldChainedFixupPointerFormat? { 20 | .init(rawValue: layout.pointer_format) 21 | } 22 | } 23 | 24 | extension DyldChainedStartsInSegment { 25 | public var swapped: Self { 26 | var layout = self.layout 27 | layout.size = layout.size.byteSwapped 28 | layout.page_size = layout.page_size.byteSwapped 29 | layout.pointer_format = layout.pointer_format.byteSwapped 30 | layout.segment_offset = layout.segment_offset.byteSwapped 31 | layout.max_valid_pointer = layout.max_valid_pointer.byteSwapped 32 | layout.page_count = layout.page_count.byteSwapped 33 | layout.page_start = layout.page_start.byteSwapped 34 | return .init(layout: layout, offset: offset, segmentIndex: segmentIndex) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldChain/DyldChainedFixupsHeader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldChainedFixupsHeader.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/01/11. 6 | // 7 | // 8 | 9 | import Foundation 10 | import MachOKitC 11 | 12 | public struct DyldChainedFixupsHeader: LayoutWrapper, Sendable { 13 | public typealias Layout = dyld_chained_fixups_header 14 | 15 | public var layout: Layout 16 | 17 | public var importsFormat: DyldChainedImportFormat? { 18 | .init(rawValue: layout.imports_format) 19 | } 20 | 21 | public var symbolsFormat: DyldChainedSymbolsFormat? { 22 | .init(rawValue: layout.symbols_format) 23 | } 24 | } 25 | 26 | extension DyldChainedFixupsHeader { 27 | public var swapped: Self { 28 | var layout = self.layout 29 | layout.fixups_version = layout.fixups_version.byteSwapped 30 | layout.starts_offset = layout.starts_offset.byteSwapped 31 | layout.imports_offset = layout.imports_offset.byteSwapped 32 | layout.symbols_offset = layout.symbols_offset.byteSwapped 33 | layout.imports_count = layout.imports_count.byteSwapped 34 | layout.imports_format = layout.imports_format.byteSwapped 35 | layout.symbols_format = layout.symbols_format.byteSwapped 36 | 37 | return .init(layout: layout) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldCache/ObjCOptimization/ObjCOptimizationFlags.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ObjCOptimizationFlags.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/10/07 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct ObjCOptimizationFlags: BitFlags { 12 | public typealias RawValue = UInt32 13 | 14 | public let rawValue: RawValue 15 | 16 | public init(rawValue: RawValue) { 17 | self.rawValue = rawValue 18 | } 19 | } 20 | 21 | // ref: https://github.com/apple-oss-distributions/dyld/blob/65bbeed63cec73f313b1d636e63f243964725a9d/include/objc-shared-cache.h#L84 22 | extension ObjCOptimizationFlags { 23 | public enum Bit: RawValue, Sendable, CaseIterable { 24 | /// never set in development cache 25 | case isProduction = 1 26 | /// set in development cache and customer 27 | case noMissingWeakSuperclasses = 2 28 | /// Shared cache was built with the new Large format 29 | case largeSharedCache = 4 30 | } 31 | } 32 | 33 | extension ObjCOptimizationFlags.Bit: CustomStringConvertible { 34 | public var description: String { 35 | switch self { 36 | case .isProduction: "IsProduction" 37 | case .noMissingWeakSuperclasses: "NoMissingWeakSuperclasses" 38 | case .largeSharedCache: "LargeSharedCache" 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/Bind/BindType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BindType.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/03. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public enum BindType: Sendable { 12 | /// BIND_TYPE_POINTER 13 | case pointer 14 | /// BIND_TYPE_TEXT_ABSOLUTE32 15 | case text_absolute32 16 | /// BIND_TYPE_TEXT_PCREL32 17 | case text_pcrel32 18 | } 19 | 20 | extension BindType: RawRepresentable { 21 | public typealias RawValue = Int32 22 | 23 | public init?(rawValue: RawValue) { 24 | switch rawValue { 25 | case BIND_TYPE_POINTER: self = .pointer 26 | case BIND_TYPE_TEXT_ABSOLUTE32: self = .text_absolute32 27 | case BIND_TYPE_TEXT_PCREL32: self = .text_pcrel32 28 | default: return nil 29 | } 30 | } 31 | 32 | public var rawValue: RawValue { 33 | switch self { 34 | case .pointer: BIND_TYPE_POINTER 35 | case .text_absolute32: BIND_TYPE_TEXT_ABSOLUTE32 36 | case .text_pcrel32: BIND_TYPE_TEXT_PCREL32 37 | } 38 | } 39 | } 40 | 41 | extension BindType: CustomStringConvertible { 42 | public var description: String { 43 | switch self { 44 | case .pointer: "BIND_TYPE_POINTER" 45 | case .text_absolute32: "BIND_TYPE_TEXT_ABSOLUTE32" 46 | case .text_pcrel32: "BIND_TYPE_TEXT_PCREL32" 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/Symbol/Nlist.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Nlist.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/08. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol NlistProtocol: LayoutWrapper, Sendable { 12 | var flags: SymbolFlags? { get } 13 | var symbolDescription: SymbolDescription? { get } 14 | var sectionNumber: Int? { get } 15 | } 16 | 17 | public struct Nlist: NlistProtocol { 18 | public typealias Layout = nlist 19 | 20 | public var layout: Layout 21 | 22 | public var flags: SymbolFlags? { 23 | .init(rawValue: numericCast(layout.n_type)) 24 | } 25 | 26 | public var symbolDescription: SymbolDescription? { 27 | .init(rawValue: numericCast(layout.n_desc)) 28 | } 29 | 30 | public var sectionNumber: Int? { 31 | layout.n_sect == NO_SECT ? nil : numericCast(layout.n_sect) 32 | } 33 | } 34 | 35 | public struct Nlist64: NlistProtocol { 36 | public typealias Layout = nlist_64 37 | 38 | public var layout: Layout 39 | 40 | public var flags: SymbolFlags? { 41 | .init(rawValue: numericCast(layout.n_type)) 42 | } 43 | 44 | public var symbolDescription: SymbolDescription? { 45 | .init(rawValue: numericCast(layout.n_desc)) 46 | } 47 | 48 | public var sectionNumber: Int? { 49 | layout.n_sect == NO_SECT ? nil : numericCast(layout.n_sect) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/Codesign/CodeSignSpecialSlotType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CodeSignSpecialSlotType.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/03/04. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /// Special slot in code directory 12 | /// 13 | /// https://github.com/apple-oss-distributions/Security/blob/ef677c3d667a44e1737c1b0245e9ed04d11c51c1/OSX/libsecurity_codesigning/lib/codedirectory.h#L86 14 | public enum CodeSignSpecialSlotType: Int, Sendable { 15 | /// Info.plist 16 | case infoSlot = 1 17 | /// internal requirements 18 | case requirementsSlot = 2 19 | /// resource directory 20 | case resourceDirSlot = 3 21 | /// Application specific slot 22 | case topDirectorySlot = 4 23 | /// embedded entitlement configuration/ 24 | case entitlementSlot = 5 25 | /// for use by disk rep/ 26 | case repSpecificSlot = 6 27 | /// DER repreesentation of entitlements/ 28 | case entitlementDERSlot = 7 29 | /// DER representation of LWCR on self/ 30 | case launchConstraintSelf = 8 31 | /// DER representation of LWCR on the parent/ 32 | case launchConstraintParent = 9 33 | /// DER representation of LWCR on the responsible process/ 34 | case launchConstraintResponsible = 10 35 | /// DER representation of LWCR on libraries loaded in the process/ 36 | case libraryConstraint = 11 37 | // (add further primary slot numbers here) 38 | } 39 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/Rebase/RebaseType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RebaseType.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/03. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public enum RebaseType: Sendable { 12 | /// REBASE_TYPE_POINTER 13 | case pointer 14 | /// REBASE_TYPE_TEXT_ABSOLUTE32 15 | case text_absolute32 16 | /// REBASE_TYPE_TEXT_PCREL32 17 | case text_pcrel32 18 | } 19 | 20 | extension RebaseType: RawRepresentable { 21 | public typealias RawValue = Int32 22 | 23 | public init?(rawValue: RawValue) { 24 | switch rawValue { 25 | case REBASE_TYPE_POINTER: self = .pointer 26 | case REBASE_TYPE_TEXT_ABSOLUTE32: self = .text_absolute32 27 | case REBASE_TYPE_TEXT_PCREL32: self = .text_pcrel32 28 | default: return nil 29 | } 30 | } 31 | 32 | public var rawValue: RawValue { 33 | switch self { 34 | case .pointer: REBASE_TYPE_POINTER 35 | case .text_absolute32: REBASE_TYPE_TEXT_ABSOLUTE32 36 | case .text_pcrel32: REBASE_TYPE_TEXT_PCREL32 37 | } 38 | } 39 | } 40 | 41 | extension RebaseType: CustomStringConvertible { 42 | public var description: String { 43 | switch self { 44 | case .pointer: "REBASE_TYPE_POINTER" 45 | case .text_absolute32: "REBASE_TYPE_TEXT_ABSOLUTE32" 46 | case .text_pcrel32: "REBASE_TYPE_TEXT_PCREL32" 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldCache/DyldCacheLocalSymbolsEntry.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldCacheLocalSymbolsEntry.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/01/15. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct DyldCacheLocalSymbolsEntry: LayoutWrapper, Sendable { 12 | public typealias Layout = dyld_cache_local_symbols_entry 13 | 14 | public var layout: Layout 15 | } 16 | 17 | extension DyldCacheLocalSymbolsEntry: DyldCacheLocalSymbolsEntryProtocol { 18 | /// Offset in cache file of start of dylib 19 | public var dylibOffset: Int { 20 | numericCast(layout.dylibOffset) 21 | } 22 | 23 | public var nlistStartIndex: Int { 24 | numericCast(layout.nlistStartIndex) 25 | } 26 | 27 | public var nlistCount: Int { 28 | numericCast(layout.nlistCount) 29 | } 30 | } 31 | 32 | public struct DyldCacheLocalSymbolsEntry64: LayoutWrapper, Sendable { 33 | public typealias Layout = dyld_cache_local_symbols_entry_64 34 | 35 | public var layout: Layout 36 | } 37 | 38 | extension DyldCacheLocalSymbolsEntry64: DyldCacheLocalSymbolsEntryProtocol { 39 | /// Offset in cache buffer of start of dylib 40 | public var dylibOffset: Int { 41 | numericCast(layout.dylibOffset) 42 | } 43 | 44 | public var nlistStartIndex: Int { 45 | numericCast(layout.nlistStartIndex) 46 | } 47 | 48 | public var nlistCount: Int { 49 | numericCast(layout.nlistCount) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/Codesign/CodeDirectory/CodeSignCodeDirectory+scatter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CodeSignCodeDirectory+scatter.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/03/11. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | extension CodeSignCodeDirectory { 12 | public func scatterOffset(in signature: MachOFile.CodeSign) -> ScatterOffset? { 13 | guard isSupportsScatter else { 14 | return nil 15 | } 16 | let layout: CS_CodeDirectory_Scatter = signature.fileSlice.ptr 17 | .advanced(by: offset) 18 | .advanced(by: layoutSize) 19 | .assumingMemoryBound(to: CS_CodeDirectory_Scatter.self) 20 | .pointee 21 | 22 | return .init( 23 | layout: signature.isSwapped ? layout.swapped : layout 24 | ) 25 | } 26 | } 27 | 28 | extension CodeSignCodeDirectory { 29 | public func scatterOffset(in signature: MachOImage.CodeSign) -> ScatterOffset? { 30 | guard isSupportsScatter else { 31 | return nil 32 | } 33 | let layout: CS_CodeDirectory_Scatter? = signature.basePointer 34 | .advanced(by: offset) 35 | .advanced(by: layoutSize) 36 | .assumingMemoryBound(to: CS_CodeDirectory_Scatter.self) 37 | .pointee 38 | guard let layout else { return nil } 39 | 40 | return .init( 41 | layout: signature.isSwapped ? layout.swapped : layout 42 | ) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /scripts/generate-symbols.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SYMBOL_DIR='./symbol-graphs' 4 | 5 | clean_build() { 6 | rm -rf ./.build 7 | } 8 | 9 | clean_xcbuild() { 10 | destination=$1 11 | scheme=$2 12 | 13 | xcodebuild -scheme "$scheme" \ 14 | -destination "generic/platform=${destination}" \ 15 | clean 16 | } 17 | 18 | clean_symbol() { 19 | rm -rf SYMBOL_DIR 20 | } 21 | 22 | generate_symbol_graphs() { 23 | destination=$1 24 | scheme=$2 25 | 26 | mkdir -p .build/symbol-graphs 27 | mkdir -p "$SYMBOL_DIR" 28 | 29 | xcodebuild clean build -scheme "${scheme}"\ 30 | -destination "generic/platform=${destination}" \ 31 | OTHER_SWIFT_FLAGS="-emit-extension-block-symbols -emit-symbol-graph -emit-symbol-graph-dir $(pwd)/.build/symbol-graphs" 32 | 33 | mv "./.build/symbol-graphs/${scheme}.symbols.json" "${SYMBOL_DIR}/${scheme}_${destination}.symbols.json" 34 | 35 | if [ -d "./Sources/$scheme/include" ]; then 36 | local HEADERS=$(ls "./Sources/$scheme/include") 37 | while IFS= read -r header; do 38 | xcrun clang \ 39 | -extract-api \ 40 | --product-name=$scheme \ 41 | -o "$SYMBOL_DIR/$scheme-$header.json" \ 42 | "$(pwd)/Sources/$scheme/include/$header" 43 | done <<<"$HEADERS" 44 | fi 45 | } 46 | 47 | 48 | clean_build 49 | clean_xcbuild ios MachOKitC 50 | clean_xcbuild ios MachOKit 51 | 52 | clean_symbol 53 | 54 | generate_symbol_graphs ios MachOKitC 55 | generate_symbol_graphs ios MachOKit 56 | -------------------------------------------------------------------------------- /Sources/MachOKit/LoadCommand/FilesetEntryCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FilesetEntryCommand.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/12/13 6 | // 7 | // 8 | 9 | public struct FilesetEntryCommand: LoadCommandWrapper { 10 | public typealias Layout = fileset_entry_command 11 | 12 | public var layout: Layout 13 | public var offset: Int // offset from mach header trailing 14 | 15 | init(_ layout: Layout, offset: Int) { 16 | self.layout = layout 17 | self.offset = offset 18 | } 19 | } 20 | 21 | extension FilesetEntryCommand { 22 | public var virtualMemoryAddress: Int { 23 | numericCast(layout.vmaddr) 24 | } 25 | 26 | public var fileOffset: Int { 27 | numericCast(layout.fileoff) 28 | } 29 | } 30 | 31 | extension FilesetEntryCommand { 32 | public func entryId(cmdsStart: UnsafeRawPointer) -> String { 33 | let ptr = cmdsStart 34 | .advanced(by: offset) 35 | .advanced(by: Int(layout.entry_id.offset)) 36 | .assumingMemoryBound(to: CChar.self) 37 | return String(cString: ptr) 38 | } 39 | } 40 | 41 | extension FilesetEntryCommand { 42 | public func entryId(in machO: MachOFile) -> String { 43 | let offset = machO.cmdsStartOffset + offset + Int(layout.entry_id.offset) 44 | return machO.fileHandle.readString( 45 | offset: numericCast(offset), 46 | size: Int(layout.cmdsize) - layoutSize 47 | ) ?? "" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Sources/MachOKit/LoadCommand/EncryptionInfoCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EncryptionInfoCommand.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/02/23. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol EncryptionInfoCommandProtocol: LoadCommandWrapper { 12 | var cryptId: Int { get } 13 | } 14 | 15 | extension EncryptionInfoCommandProtocol { 16 | public var isEncrypted: Bool { 17 | cryptId != 0 18 | } 19 | } 20 | 21 | public struct EncryptionInfoCommand: LoadCommandWrapper { 22 | public typealias Layout = encryption_info_command 23 | 24 | public var layout: Layout 25 | public var offset: Int // offset from mach header trailing 26 | 27 | init(_ layout: Layout, offset: Int) { 28 | self.layout = layout 29 | self.offset = offset 30 | } 31 | } 32 | 33 | public struct EncryptionInfoCommand64: LoadCommandWrapper { 34 | public typealias Layout = encryption_info_command_64 35 | 36 | public var layout: Layout 37 | public var offset: Int // offset from mach header trailing 38 | 39 | init(_ layout: Layout, offset: Int) { 40 | self.layout = layout 41 | self.offset = offset 42 | } 43 | } 44 | 45 | extension EncryptionInfoCommand: EncryptionInfoCommandProtocol { 46 | public var cryptId: Int { 47 | numericCast(layout.cryptid) 48 | } 49 | } 50 | 51 | extension EncryptionInfoCommand64: EncryptionInfoCommandProtocol { 52 | public var cryptId: Int { 53 | numericCast(layout.cryptid) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/Export/ExportSymbolKind.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExportSymbolKind.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/03. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public enum ExportSymbolKind: Sendable { 12 | /// EXPORT_SYMBOL_FLAGS_KIND_REGULAR 13 | case regular 14 | /// EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL 15 | case thread_local 16 | /// EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 17 | case absolute 18 | } 19 | 20 | extension ExportSymbolKind: RawRepresentable { 21 | public typealias RawValue = Int32 22 | 23 | public init?(rawValue: RawValue) { 24 | switch rawValue { 25 | case EXPORT_SYMBOL_FLAGS_KIND_REGULAR: self = .regular 26 | case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: self = .thread_local 27 | case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE: self = .absolute 28 | default: return nil 29 | } 30 | } 31 | 32 | public var rawValue: RawValue { 33 | switch self { 34 | case .regular: EXPORT_SYMBOL_FLAGS_KIND_REGULAR 35 | case .thread_local: EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL 36 | case .absolute: EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 37 | } 38 | } 39 | } 40 | 41 | extension ExportSymbolKind: CustomStringConvertible { 42 | public var description: String { 43 | switch self { 44 | case .regular: "EXPORT_SYMBOL_FLAGS_KIND_REGULAR" 45 | case .thread_local: "EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL" 46 | case .absolute: "EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE" 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/Symbol/SymbolLibraryOrdinalType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SymbolLibraryOrdinalType.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/08. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public enum SymbolLibraryOrdinalType: Sendable { 12 | /// SELF_LIBRARY_ORDINAL 13 | case `self` 14 | /// DYNAMIC_LOOKUP_ORDINAL 15 | case dynamic_lookup_ordinal 16 | /// EXECUTABLE_ORDINAL 17 | case executable_ordinal 18 | } 19 | 20 | extension SymbolLibraryOrdinalType: RawRepresentable { 21 | public typealias RawValue = Int32 22 | 23 | public init?(rawValue: RawValue) { 24 | switch rawValue { 25 | case RawValue(SELF_LIBRARY_ORDINAL): self = .`self` 26 | case RawValue(DYNAMIC_LOOKUP_ORDINAL): self = .dynamic_lookup_ordinal 27 | case RawValue(EXECUTABLE_ORDINAL): self = .executable_ordinal 28 | default: return nil 29 | } 30 | } 31 | 32 | public var rawValue: RawValue { 33 | switch self { 34 | case .`self`: RawValue(SELF_LIBRARY_ORDINAL) 35 | case .dynamic_lookup_ordinal: RawValue(DYNAMIC_LOOKUP_ORDINAL) 36 | case .executable_ordinal: RawValue(EXECUTABLE_ORDINAL) 37 | } 38 | } 39 | } 40 | 41 | extension SymbolLibraryOrdinalType: CustomStringConvertible { 42 | public var description: String { 43 | switch self { 44 | case .self: "SELF_LIBRARY_ORDINAL" 45 | case .dynamic_lookup_ordinal: "DYNAMIC_LOOKUP_ORDINAL" 46 | case .executable_ordinal: "EXECUTABLE_ORDINAL" 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldCache/DyldCacheImageInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldCacheImageInfo.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/01/15. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct DyldCacheImageInfo: LayoutWrapper, Sendable { 12 | public typealias Layout = dyld_cache_image_info 13 | 14 | public var layout: Layout 15 | } 16 | 17 | extension DyldCacheImageInfo { 18 | /// Path for image 19 | /// - Parameter cache: DyldCache to which this image belongs 20 | /// - Returns: Path for image 21 | public func path(in cache: DyldCache) -> String? { 22 | _path(in: cache) 23 | } 24 | 25 | /// Path for image 26 | /// - Parameter cache: DyldCache to which this image belongs 27 | /// - Returns: Path for image 28 | public func path(in cache: DyldCacheLoaded) -> String? { 29 | String( 30 | cString: cache.ptr 31 | .advanced(by: numericCast(layout.pathFileOffset)) 32 | .assumingMemoryBound(to: CChar.self) 33 | ) 34 | } 35 | 36 | /// Path for image 37 | /// - Parameter cache: FullDyldCache to which this image belongs 38 | /// - Returns: Path for image 39 | public func path(in cache: FullDyldCache) -> String? { 40 | _path(in: cache) 41 | } 42 | } 43 | 44 | extension DyldCacheImageInfo { 45 | internal func _path(in cache: Cache) -> String? { 46 | cache.fileHandle.readString( 47 | offset: numericCast(layout.pathFileOffset) 48 | ) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Sources/MachOKit/Header/FatHeader/FatHeader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FatHeader.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/04. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct FatHeader: LayoutWrapper, Sendable { 12 | public var layout: fat_header 13 | 14 | public var magic: Magic! { 15 | .init(rawValue: layout.magic) 16 | } 17 | } 18 | 19 | extension FatHeader { 20 | public func arches(data: Data, isSwapped: Bool) -> [FatArch] { 21 | if magic.is64BitFat { 22 | return data.withUnsafeBytes { 23 | let ptr = $0.bindMemory(to: fat_arch_64.self) 24 | 25 | if isSwapped { 26 | swap_fat_arch_64(.init(mutating: ptr.baseAddress), layout.nfat_arch, NXHostByteOrder()) 27 | } 28 | 29 | guard let baseAddress = ptr.baseAddress else { return [] } 30 | 31 | return ptr.indices.map { 32 | let layout = UnsafeRawPointer(baseAddress.advanced(by: $0)) 33 | .bindMemory(to: fat_arch.self, capacity: 1) 34 | .pointee 35 | return .init(layout: layout) 36 | } 37 | } 38 | } else { 39 | return data.withUnsafeBytes { 40 | let ptr = $0.bindMemory(to: fat_arch.self) 41 | 42 | if isSwapped { 43 | swap_fat_arch(.init(mutating: ptr.baseAddress), layout.nfat_arch, NXHostByteOrder()) 44 | } 45 | 46 | return ptr 47 | .map { .init(layout: $0) } 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/MachOKit/Header/CPU.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CPU.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/11/29. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct CPU: Sendable, Equatable { 12 | public let typeRawValue: cpu_type_t 13 | public let subtypeRawValue: cpu_subtype_t 14 | 15 | public var type: CPUType? { 16 | .init(rawValue: typeRawValue) 17 | } 18 | 19 | public var subtype: CPUSubType? { 20 | if let type { 21 | let subtypeRaw = (cpu_subtype_t(subtypeRawValue) & cpu_subtype_t(~CPU_SUBTYPE_MASK)) 22 | return .init(rawValue: subtypeRaw, of: type) 23 | } 24 | return nil 25 | } 26 | } 27 | 28 | extension CPU: CustomStringConvertible { 29 | public var description: String { 30 | let type = type?.description ?? "unknown\(typeRawValue)" 31 | let subtype = subtype?.description ?? "unknown" 32 | 33 | return "\(type)(\(subtype))" 34 | } 35 | } 36 | 37 | extension CPU { 38 | public var is64Bit: Bool { 39 | typeRawValue & CPU_ARCH_ABI64 != 0 40 | } 41 | 42 | public var is64BitHardwareWith32BitType: Bool { 43 | typeRawValue & CPU_ARCH_ABI64_32 != 0 44 | } 45 | } 46 | 47 | #if canImport(Darwin) 48 | extension CPU { 49 | /// CPU type and subtype of host pc 50 | public static var current: CPU? { 51 | guard let type: CPUType = .current, 52 | let subtype: CPUSubType = .current else { 53 | return nil 54 | } 55 | return .init( 56 | typeRawValue: type.rawValue, 57 | subtypeRawValue: subtype.rawValue 58 | ) 59 | } 60 | } 61 | #endif 62 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/Codesign/CodeDirectory/CodeSignCodeDirectory+codeLimit64.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CodeSignCodeDirectory+codeLimit64.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/03/11. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | extension CodeSignCodeDirectory { 12 | public func codeLimit64(in signature: MachOFile.CodeSign) -> CodeLimit64? { 13 | guard isSupportsCodeLimit64 else { 14 | return nil 15 | } 16 | let layout: CS_CodeDirectory_CodeLimit64 = signature.fileSlice.ptr 17 | .advanced(by: offset) 18 | .advanced(by: layoutSize) 19 | .advanced(by: ScatterOffset.layoutSize) 20 | .advanced(by: TeamIdOffset.layoutSize) 21 | .assumingMemoryBound(to: CS_CodeDirectory_CodeLimit64.self) 22 | .pointee 23 | return .init( 24 | layout: signature.isSwapped ? layout.swapped : layout 25 | ) 26 | } 27 | } 28 | 29 | extension CodeSignCodeDirectory { 30 | public func codeLimit64(in signature: MachOImage.CodeSign) -> CodeLimit64? { 31 | guard isSupportsCodeLimit64 else { 32 | return nil 33 | } 34 | let layout: CS_CodeDirectory_CodeLimit64? = signature.basePointer 35 | .advanced(by: offset) 36 | .advanced(by: layoutSize) 37 | .advanced(by: ScatterOffset.layoutSize) 38 | .advanced(by: TeamIdOffset.layoutSize) 39 | .assumingMemoryBound(to: CS_CodeDirectory_CodeLimit64.self) 40 | .pointee 41 | guard let layout else { return nil } 42 | return .init( 43 | layout: signature.isSwapped ? layout.swapped : layout 44 | ) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/MachOKit/Util/SwiftDemangle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftDemangle.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/01/05. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | @_silgen_name("swift_demangle") 12 | internal func _stdlib_demangleImpl( 13 | mangledName: UnsafePointer?, 14 | mangledNameLength: UInt, 15 | outputBuffer: UnsafeMutablePointer?, 16 | outputBufferSize: UnsafeMutablePointer?, 17 | flags: UInt32 18 | ) -> UnsafeMutablePointer? 19 | 20 | internal func stdlib_demangleName( 21 | _ mangledName: String 22 | ) -> String { 23 | guard !mangledName.isEmpty else { return mangledName } 24 | return mangledName.utf8CString.withUnsafeBufferPointer { mangledNameUTF8 in 25 | let demangledNamePtr = _stdlib_demangleImpl( 26 | mangledName: mangledNameUTF8.baseAddress, 27 | mangledNameLength: numericCast(mangledNameUTF8.count - 1), 28 | outputBuffer: nil, 29 | outputBufferSize: nil, 30 | flags: 0 31 | ) 32 | 33 | if let demangledNamePtr { 34 | return String(cString: demangledNamePtr) 35 | } 36 | return mangledName 37 | } 38 | } 39 | 40 | internal func stdlib_demangleName( 41 | _ mangledName: UnsafePointer 42 | ) -> UnsafePointer { 43 | 44 | let demangledNamePtr = _stdlib_demangleImpl( 45 | mangledName: mangledName, 46 | mangledNameLength: numericCast(strlen(mangledName)), 47 | outputBuffer: nil, 48 | outputBufferSize: nil, 49 | flags: 0 50 | ) 51 | if let demangledNamePtr { 52 | return .init(demangledNamePtr) 53 | } 54 | return mangledName 55 | } 56 | -------------------------------------------------------------------------------- /.github/workflows/update-prebuild-spm.yml: -------------------------------------------------------------------------------- 1 | name: Update Prebuilt SPM 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | update-binary-target: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Extract Release Information 13 | id: release_info 14 | env: 15 | VERSION: ${{ github.event.release.tag_name }} 16 | RELEASE_BODY: ${{ github.event.release.body }} 17 | run: | 18 | MACHOKIT_CHECKSUM=$(echo "$RELEASE_BODY" | awk -v target="MachOKit" ' 19 | found { print; exit } 20 | $0 == target { found=1 } 21 | ') 22 | MACHOKITC_CHECKSUM=$(echo "$RELEASE_BODY" | awk -v target="MachOKitC" ' 23 | found { print; exit } 24 | $0 == target { found=1 } 25 | ') 26 | 27 | echo "VERSION=${VERSION}" >> $GITHUB_ENV 28 | echo "MACHOKIT_CHECKSUM=${MACHOKIT_CHECKSUM}" >> $GITHUB_ENV 29 | echo "MACHOKITC_CHECKSUM=${MACHOKITC_CHECKSUM}" >> $GITHUB_ENV 30 | 31 | - name: Send repository_dispatch event 32 | run: | 33 | curl -X POST -H "Accept: application/vnd.github.v3+json" \ 34 | -H "Authorization: token ${{ secrets.PERSONAL_ACCESS_TOKEN }}" \ 35 | https://api.github.com/repos/p-x9/MachOKit-SPM/dispatches \ 36 | --data "$(jq -n \ 37 | --arg version "$VERSION" \ 38 | --arg machOKitChecksum "$MACHOKIT_CHECKSUM" \ 39 | --arg machOKitCChecksum "$MACHOKITC_CHECKSUM" \ 40 | '{event_type: "update_binary_version", client_payload: {version: $version, "mach-o_kit_checksum": $machOKitChecksum, "mach-o_kit_c_checksum": $machOKitCChecksum}}')" 41 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/Symbol/SymbolType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SymbolType.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/08. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public enum SymbolType: Sendable { 12 | /// N_UNDF 13 | /// undefined, n_sect == NO_SECT 14 | case undf 15 | 16 | /// N_ABS 17 | /// absolute, n_sect == NO_SECT 18 | case abs 19 | 20 | /// N_SECT 21 | /// defined in section number n_sect 22 | case sect 23 | 24 | /// N_PBUD 25 | /// prebound undefined (defined in a dylib) 26 | case pbud 27 | 28 | /// N_INDR 29 | /// indirect 30 | case indr 31 | } 32 | 33 | extension SymbolType: RawRepresentable { 34 | public typealias RawValue = Int32 35 | 36 | public init?(rawValue: RawValue) { 37 | switch rawValue { 38 | case RawValue(N_UNDF): self = .undf 39 | case RawValue(N_ABS): self = .abs 40 | case RawValue(N_SECT): self = .sect 41 | case RawValue(N_PBUD): self = .pbud 42 | case RawValue(N_INDR): self = .indr 43 | default: return nil 44 | } 45 | } 46 | 47 | public var rawValue: RawValue { 48 | switch self { 49 | case .undf: RawValue(N_UNDF) 50 | case .abs: RawValue(N_ABS) 51 | case .sect: RawValue(N_SECT) 52 | case .pbud: RawValue(N_PBUD) 53 | case .indr: RawValue(N_PBUD) 54 | } 55 | } 56 | } 57 | 58 | extension SymbolType: CustomStringConvertible { 59 | public var description: String { 60 | switch self { 61 | case .undf: "N_UNDF" 62 | case .abs: "N_ABS" 63 | case .sect: "N_SECT" 64 | case .pbud: "N_PBUD" 65 | case .indr: "N_PBUD" 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/AotCache/AotCodeFragment.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AotCodeFragment.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/12/20 6 | // 7 | // 8 | 9 | import MachOKitC 10 | 11 | public struct AotCodeFragment: LayoutWrapper { 12 | public typealias Layout = aot_code_fragment_metadata 13 | 14 | public var layout: Layout 15 | public let offset: Int 16 | } 17 | 18 | extension AotCodeFragment { 19 | public func instructionMap( 20 | in machO: MachOFile 21 | ) -> AotInstructionMap? { 22 | guard layout.instruction_map_size > 0 else { return nil } 23 | let offset = machO.headerStartOffset + linkeditOffset(in: machO) + numericCast(layout.instruction_map_offset) 24 | return .init( 25 | header: try! machO.fileHandle.read(offset: offset), 26 | offset: offset 27 | ) 28 | } 29 | } 30 | 31 | extension AotCodeFragment { 32 | public func branchData( 33 | in machO: MachOFile 34 | ) -> AotBranchData? { 35 | guard layout.branch_data_size > 0 else { return nil } 36 | let offset = machO.headerStartOffset + linkeditOffset(in: machO) + numericCast(layout.branch_data_offset) 37 | return .init( 38 | header: try! machO.fileHandle.read(offset: offset), 39 | offset: offset 40 | ) 41 | } 42 | } 43 | 44 | extension AotCodeFragment { 45 | private func linkeditOffset(in machO: MachOFile) -> Int { 46 | let loadCommands = machO.loadCommands 47 | return if let linkedit = loadCommands.linkedit { 48 | linkedit.fileOffset 49 | } else if let linkedit = loadCommands.linkedit64 { 50 | linkedit.fileOffset 51 | } else { 0 } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/Codesign/CodeSignHashType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CodeSignHashType.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/03/03. 6 | // 7 | // 8 | 9 | import Foundation 10 | import MachOKitC 11 | 12 | public enum CodeSignHashType: Sendable { 13 | case sha1 14 | case sha256 15 | case sha256_truncated 16 | case sha384 17 | } 18 | 19 | extension CodeSignHashType: RawRepresentable { 20 | public typealias RawValue = UInt32 21 | 22 | public init?(rawValue: UInt32) { 23 | switch rawValue { 24 | case CS_HASHTYPE_SHA1: self = .sha1 25 | case CS_HASHTYPE_SHA256: self = .sha256 26 | case CS_HASHTYPE_SHA256_TRUNCATED: self = .sha256_truncated 27 | case CS_HASHTYPE_SHA384: self = .sha384 28 | default: 29 | return nil 30 | } 31 | } 32 | 33 | public var rawValue: UInt32 { 34 | switch self { 35 | case .sha1: CS_HASHTYPE_SHA1 36 | case .sha256: CS_HASHTYPE_SHA256 37 | case .sha256_truncated: CS_HASHTYPE_SHA256_TRUNCATED 38 | case .sha384: CS_HASHTYPE_SHA384 39 | } 40 | } 41 | } 42 | 43 | extension CodeSignHashType: CustomStringConvertible { 44 | public var description: String { 45 | switch self { 46 | case .sha1: "CS_HASHTYPE_SHA1" 47 | case .sha256: "CS_HASHTYPE_SHA256" 48 | case .sha256_truncated: "CS_HASHTYPE_SHA256_TRUNCATED" 49 | case .sha384: "CS_HASHTYPE_SHA384" 50 | } 51 | } 52 | } 53 | 54 | extension CodeSignHashType { 55 | package var priority: Int { 56 | switch self { 57 | case .sha1: 0 58 | case .sha256_truncated: 1 59 | case .sha256: 2 60 | case .sha384: 3 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldCache/SlideInfo/DyldCacheSlideInfo3.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldCacheSlideInfo3.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/07/24 6 | // 7 | // 8 | 9 | import Foundation 10 | import MachOKitC 11 | 12 | public struct DyldCacheSlideInfo3: LayoutWrapper, Sendable { 13 | public typealias Layout = dyld_cache_slide_info3 14 | 15 | public var layout: Layout 16 | public var offset: Int 17 | } 18 | 19 | // MARK: - PageStart 20 | extension DyldCacheSlideInfo3 { 21 | public struct PageStart { 22 | public let value: UInt16 23 | 24 | public var isNoRebase: Bool { 25 | value == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE 26 | } 27 | } 28 | } 29 | 30 | // MARK: - function & proerty 31 | extension DyldCacheSlideInfo3 { 32 | public var pageSize: Int { 33 | numericCast(layout.page_size) 34 | } 35 | } 36 | 37 | extension DyldCacheSlideInfo3 { 38 | public var numberOfPageStarts: Int { 39 | numericCast(layout.page_starts_count) 40 | } 41 | 42 | public func pageStarts(in cache: DyldCache) -> DataSequence? { 43 | _pageStarts(in: cache) 44 | } 45 | 46 | public func pageStarts(in cache: FullDyldCache) -> DataSequence? { 47 | _pageStarts(in: cache) 48 | } 49 | } 50 | 51 | extension DyldCacheSlideInfo3 { 52 | internal func _pageStarts( 53 | in cache: Cache 54 | ) -> DataSequence? { 55 | let pageStartsOffset = layoutSize 56 | return cache.fileHandle.readDataSequence( 57 | offset: numericCast(offset) + numericCast(pageStartsOffset), 58 | numberOfElements: numberOfPageStarts 59 | ) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldCache/SlideInfo/DyldCacheSlideInfo5.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldCacheSlideInfo5.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/07/25 6 | // 7 | // 8 | 9 | import Foundation 10 | import MachOKitC 11 | 12 | public struct DyldCacheSlideInfo5: LayoutWrapper, Sendable { 13 | public typealias Layout = dyld_cache_slide_info5 14 | 15 | public var layout: Layout 16 | public var offset: Int 17 | } 18 | 19 | // MARK: - PageStart 20 | extension DyldCacheSlideInfo5 { 21 | public struct PageStart { 22 | public let value: UInt16 23 | 24 | public var isNoRebase: Bool { 25 | value == DYLD_CACHE_SLIDE_V5_PAGE_ATTR_NO_REBASE 26 | } 27 | } 28 | } 29 | 30 | // MARK: - function & proerty 31 | extension DyldCacheSlideInfo5 { 32 | public var pageSize: Int { 33 | numericCast(layout.page_size) 34 | } 35 | } 36 | 37 | extension DyldCacheSlideInfo5 { 38 | public var numberOfPageStarts: Int { 39 | numericCast(layout.page_starts_count) 40 | } 41 | 42 | public func pageStarts(in cache: DyldCache) -> DataSequence? { 43 | _pageStarts(in: cache) 44 | } 45 | 46 | public func pageStarts(in cache: FullDyldCache) -> DataSequence? { 47 | _pageStarts(in: cache) 48 | } 49 | } 50 | 51 | extension DyldCacheSlideInfo5 { 52 | internal func _pageStarts( 53 | in cache: Cache 54 | ) -> DataSequence? { 55 | let pageStartsOffset = layoutSize 56 | return cache.fileHandle.readDataSequence( 57 | offset: numericCast(offset) + numericCast(pageStartsOffset), 58 | numberOfElements: numberOfPageStarts 59 | ) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldCache/DyldCacheImageTextInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldCacheImageTextInfo.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/01/15. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct DyldCacheImageTextInfo: LayoutWrapper, Sendable { 12 | public typealias Layout = dyld_cache_image_text_info 13 | 14 | public var layout: Layout 15 | } 16 | 17 | extension DyldCacheImageTextInfo { 18 | /// UUID of this image text 19 | public var uuid: UUID { 20 | .init(uuid: layout.uuid) 21 | } 22 | 23 | /// Path for image text 24 | /// - Parameter cache: DyldCache to which this image belongs 25 | /// - Returns: Path for image text 26 | public func path(in cache: DyldCache) -> String? { 27 | _path(in: cache) 28 | } 29 | 30 | /// Path for image text 31 | /// - Parameter cache: DyldCache to which this image belongs 32 | /// - Returns: Path for image text 33 | public func path(in cache: FullDyldCache) -> String? { 34 | _path(in: cache) 35 | } 36 | 37 | /// Path for image text 38 | /// - Parameter cache: DyldCache to which this image belongs 39 | /// - Returns: Path for image text 40 | public func path(in cache: DyldCacheLoaded) -> String? { 41 | String( 42 | cString: cache.ptr 43 | .advanced(by: numericCast(layout.pathOffset)) 44 | .assumingMemoryBound(to: CChar.self) 45 | ) 46 | } 47 | } 48 | 49 | extension DyldCacheImageTextInfo { 50 | internal func _path( 51 | in cache: Cache 52 | ) -> String? { 53 | cache.fileHandle.readString( 54 | offset: numericCast(layout.pathOffset) 55 | ) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Sources/MachOKit/MachOImage+LoadCommands.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MachOImage+LoadCommands.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/11/28. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | extension MachOImage { 12 | public struct LoadCommands: Sequence { 13 | public let start: UnsafeRawPointer 14 | public let numberOfCommands: Int 15 | 16 | public func makeIterator() -> Iterator { 17 | Iterator( 18 | start: start, 19 | numberOfCommands: numberOfCommands 20 | ) 21 | } 22 | } 23 | } 24 | 25 | extension MachOImage.LoadCommands { 26 | public struct Iterator: IteratorProtocol { 27 | public typealias Element = LoadCommand 28 | 29 | public let start: UnsafeRawPointer 30 | public let numberOfCommands: Int 31 | 32 | private var nextOffset: Int = 0 33 | private var nextIndex: Int = 0 34 | 35 | public init( 36 | start: UnsafeRawPointer, 37 | numberOfCommands: Int 38 | ) { 39 | self.start = start 40 | self.numberOfCommands = numberOfCommands 41 | } 42 | 43 | public mutating func next() -> Element? { 44 | guard nextIndex < numberOfCommands else { 45 | return nil 46 | } 47 | let ptr = start.advanced(by: nextOffset) 48 | .assumingMemoryBound(to: load_command.self) 49 | 50 | defer { 51 | nextOffset += numericCast(ptr.pointee.cmdsize) 52 | nextIndex += 1 53 | } 54 | 55 | return LoadCommand.convert(ptr, offset: nextOffset) 56 | } 57 | } 58 | } 59 | 60 | extension MachOImage.LoadCommands: LoadCommandsProtocol {} 61 | -------------------------------------------------------------------------------- /Sources/MachOKit/FatFile.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FatFile.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/04. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public class FatFile { 12 | let url: URL 13 | let fileHandle: FileHandle 14 | 15 | public private(set) var isSwapped: Bool 16 | public let header: FatHeader 17 | 18 | public var is64bit: Bool { header.magic.is64BitFat } 19 | public var headerSize: Int { 20 | MemoryLayout.size 21 | } 22 | public var archesSize: Int { 23 | Int(header.nfat_arch) * (is64bit ? MemoryLayout.size : MemoryLayout.size) 24 | } 25 | 26 | public var archesStartOffset: Int { 27 | headerSize 28 | } 29 | 30 | public var arches: [FatArch] { 31 | let data = fileHandle.readData( 32 | offset: UInt64(archesStartOffset), 33 | size: archesSize 34 | ) 35 | return header.arches(data: data, isSwapped: isSwapped) 36 | } 37 | 38 | init(url: URL) throws { 39 | self.url = url 40 | self.fileHandle = try FileHandle(forReadingFrom: url) 41 | 42 | var header: FatHeader = fileHandle.read( 43 | offset: 0 44 | ) 45 | 46 | let isSwapped = header.magic.isSwapped 47 | if isSwapped { 48 | swap_fat_header(&header.layout, NXHostByteOrder()) 49 | } 50 | 51 | self.isSwapped = isSwapped 52 | self.header = header 53 | } 54 | 55 | deinit { 56 | fileHandle.closeFile() 57 | } 58 | } 59 | 60 | extension FatFile { 61 | public func machOFiles() throws -> [MachOFile] { 62 | try arches.map { 63 | try .init(url: url, headerStartOffset: Int($0.offset)) 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Sources/MachOKit/LoadCommand/LinkerOptionCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LinkerOptionCommand.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/18. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct LinkerOptionCommand: LoadCommandWrapper { 12 | public typealias Layout = linker_option_command 13 | 14 | public var layout: Layout 15 | public var offset: Int // offset from mach header trailing 16 | 17 | init(_ layout: Layout, offset: Int) { 18 | self.layout = layout 19 | self.offset = offset 20 | } 21 | } 22 | 23 | extension LinkerOptionCommand { 24 | public func options(cmdsStart: UnsafeRawPointer) -> [String] { 25 | // swiftlint:disable:next empty_count 26 | guard layout.count > 0 else { return [] } 27 | 28 | let ptr = cmdsStart 29 | .advanced(by: offset) 30 | .advanced(by: layoutSize) 31 | .assumingMemoryBound(to: UInt8.self) 32 | let strings = MachOImage.Strings( 33 | basePointer: ptr, 34 | tableSize: Int(layout.cmdsize) - layoutSize 35 | ).map(\.string) 36 | 37 | return Array(strings[0.. [String] { 41 | // swiftlint:disable:next empty_count 42 | guard layout.count > 0 else { return [] } 43 | 44 | let offset = machO.cmdsStartOffset + offset + layoutSize 45 | let size = Int(layout.cmdsize) - layoutSize 46 | 47 | let strings = MachOFile.Strings( 48 | machO: machO, 49 | offset: offset, 50 | size: size, 51 | isSwapped: machO.isSwapped 52 | ).map(\.string) 53 | 54 | return Array(strings[0.. ExecutableSegment? { 13 | guard isSupportsExecSegment else { 14 | return nil 15 | } 16 | let layout: CS_CodeDirectory_ExecSeg = signature.fileSlice.ptr 17 | .advanced(by: offset) 18 | .advanced(by: layoutSize) 19 | .advanced(by: ScatterOffset.layoutSize) 20 | .advanced(by: TeamIdOffset.layoutSize) 21 | .advanced(by: CodeLimit64.layoutSize) 22 | .assumingMemoryBound(to: CS_CodeDirectory_ExecSeg.self) 23 | .pointee 24 | return .init( 25 | layout: signature.isSwapped ? layout.swapped : layout 26 | ) 27 | } 28 | } 29 | 30 | extension CodeSignCodeDirectory { 31 | public func executableSegment(in signature: MachOImage.CodeSign) -> ExecutableSegment? { 32 | guard isSupportsExecSegment else { 33 | return nil 34 | } 35 | let layout: CS_CodeDirectory_ExecSeg? = signature.basePointer 36 | .advanced(by: offset) 37 | .advanced(by: layoutSize) 38 | .advanced(by: ScatterOffset.layoutSize) 39 | .advanced(by: TeamIdOffset.layoutSize) 40 | .advanced(by: CodeLimit64.layoutSize) 41 | .assumingMemoryBound(to: CS_CodeDirectory_ExecSeg.self) 42 | .pointee 43 | guard let layout else { return nil } 44 | return .init( 45 | layout: signature.isSwapped ? layout.swapped : layout 46 | ) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/Codesign/CodeSignGenericBlob.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CodeSignGenericBlob.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/03/04. 6 | // 7 | // 8 | 9 | import Foundation 10 | import MachOKitC 11 | 12 | public struct CodeSignGenericBlob: LayoutWrapper, Sendable { 13 | public typealias Layout = CS_GenericBlob 14 | 15 | public var layout: Layout 16 | } 17 | 18 | extension CodeSignGenericBlob { 19 | public var isSwapped: Bool { 20 | layout.isSwapped 21 | } 22 | 23 | public var magic: CodeSignMagic! { 24 | .init(rawValue: isSwapped ? layout.magic.byteSwapped : layout.magic) 25 | } 26 | 27 | public var length: Int { 28 | numericCast(isSwapped ? layout.length.byteSwapped : layout.length) 29 | } 30 | } 31 | 32 | extension CodeSignGenericBlob { 33 | static func load( 34 | from baseAddress: UnsafeRawPointer, 35 | offset: Int, 36 | isSwapped: Bool 37 | ) -> CodeSignGenericBlob? { 38 | let ptr = baseAddress.advanced(by: offset) 39 | var _magic = ptr 40 | .assumingMemoryBound(to: UInt32.self) 41 | .pointee 42 | if isSwapped { _magic = _magic.byteSwapped } 43 | guard CodeSignMagic(rawValue: _magic) != nil else { 44 | return nil 45 | } 46 | let layout = ptr 47 | .assumingMemoryBound(to: CS_GenericBlob.self) 48 | .pointee 49 | return .init( 50 | layout: isSwapped ? layout.swapped : layout 51 | ) 52 | } 53 | } 54 | 55 | extension CS_GenericBlob { 56 | var isSwapped: Bool { 57 | magic < 0xfade0000 58 | } 59 | 60 | var swapped: CS_GenericBlob { 61 | var swapped = CS_GenericBlob() 62 | swapped.magic = magic.byteSwapped 63 | swapped.length = length.byteSwapped 64 | return swapped 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Sources/MachOKit/LoadCommand/AotMetadataCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AotMetadataCommand.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/12/20 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct AotMetadataCommand: LoadCommandWrapper { 12 | public typealias Layout = aot_metadata_command 13 | 14 | public var layout: Layout 15 | public var offset: Int // offset from mach header trailing 16 | 17 | init(_ layout: Layout, offset: Int) { 18 | self.layout = layout 19 | self.offset = offset 20 | } 21 | } 22 | 23 | extension AotMetadataCommand { 24 | public func imagePath( 25 | in machO: MachOFile 26 | ) -> String? { 27 | let linkeditOffset = linkeditOffset(in: machO) 28 | 29 | let offset = machO.headerStartOffset + linkeditOffset + Int(layout.x86_image_path_offset) 30 | return machO.fileHandle.readString( 31 | offset: numericCast(offset), 32 | size: Int(layout.x86_image_path_size) 33 | ) ?? "" 34 | } 35 | } 36 | 37 | extension AotMetadataCommand { 38 | public func codeFragment( 39 | in machO: MachOFile 40 | ) -> AotCodeFragment? { 41 | let linkeditOffset = linkeditOffset(in: machO) 42 | 43 | let offset = machO.headerStartOffset + linkeditOffset + Int(layout.fragment_offset) 44 | 45 | return .init( 46 | layout: try! machO.fileHandle.read(offset: offset), 47 | offset: offset 48 | ) 49 | } 50 | } 51 | 52 | extension AotMetadataCommand { 53 | private func linkeditOffset(in machO: MachOFile) -> Int { 54 | let loadCommands = machO.loadCommands 55 | return if let linkedit = loadCommands.linkedit { 56 | linkedit.fileOffset 57 | } else if let linkedit = loadCommands.linkedit64 { 58 | linkedit.fileOffset 59 | } else { 0 } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Sources/MachOKit/Util/TrieTree/MemoryTrieTree.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MemoryTrieTree.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/07/05 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct MemoryTrieTree: TrieTreeProtocol { 12 | public let basePointer: UnsafeRawPointer 13 | public let size: Int 14 | 15 | @_spi(Support) 16 | public init(basePointer: UnsafeRawPointer, size: Int) { 17 | self.basePointer = basePointer 18 | self.size = size 19 | } 20 | } 21 | 22 | extension MemoryTrieTree { 23 | public func element(atOffset offset: Int) -> TrieNode? { 24 | var nextOffset: Int = offset 25 | return .readNext( 26 | basePointer: basePointer.assumingMemoryBound(to: UInt8.self), 27 | trieSize: size, 28 | nextOffset: &nextOffset 29 | ) 30 | } 31 | } 32 | 33 | extension MemoryTrieTree: Sequence { 34 | public typealias Element = TrieNode 35 | 36 | public func makeIterator() -> Iterator { 37 | .init(basePointer: basePointer, size: size) 38 | } 39 | } 40 | 41 | extension MemoryTrieTree { 42 | public struct Iterator: IteratorProtocol { 43 | public let basePointer: UnsafeRawPointer 44 | public let size: Int 45 | 46 | internal var nextOffset: Int = 0 47 | 48 | @_spi(Support) 49 | public init(basePointer: UnsafeRawPointer, size: Int) { 50 | self.basePointer = basePointer 51 | self.size = size 52 | } 53 | 54 | public mutating func next() -> Element? { 55 | guard nextOffset < size else { return nil } 56 | 57 | return .readNext( 58 | basePointer: basePointer.assumingMemoryBound(to: UInt8.self), 59 | trieSize: size, 60 | nextOffset: &nextOffset 61 | ) 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Sources/MachOKit/MachOFile+BindOperations.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MachOFile+BindOperations.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/09. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | extension MachOFile { 12 | public struct BindOperations: Sequence { 13 | public let data: Data 14 | public let bindOffset: Int 15 | public let bindSize: Int 16 | 17 | public func makeIterator() -> Iterator { 18 | .init(data: data) 19 | } 20 | } 21 | } 22 | 23 | extension MachOFile.BindOperations { 24 | init( 25 | machO: MachOFile, 26 | info: dyld_info_command, 27 | kind: BindOperationsKind = .normal 28 | ) { 29 | let bindOffset = Int(kind.bindOffset(of: info)) 30 | let bindSize = Int(kind.bindSize(of: info)) 31 | let offset = machO.headerStartOffset + bindOffset 32 | let data = try! machO.fileHandle.readData( 33 | offset: offset, 34 | length: bindSize 35 | ) 36 | self.init(data: data, bindOffset: bindOffset, bindSize: bindSize) 37 | } 38 | } 39 | 40 | extension MachOFile.BindOperations { 41 | public struct Iterator: IteratorProtocol { 42 | public typealias Element = BindOperation 43 | 44 | private let data: Data 45 | private var nextOffset: Int = 0 46 | 47 | init(data: Data) { 48 | self.data = data 49 | } 50 | 51 | public mutating func next() -> Element? { 52 | guard nextOffset < data.count else { return nil } 53 | 54 | return data.withUnsafeBytes { 55 | guard let basePointer = $0.baseAddress else { return nil } 56 | 57 | return BindOperation.readNext( 58 | basePointer: basePointer.assumingMemoryBound(to: UInt8.self), 59 | bindSize: data.count, 60 | nextOffset: &nextOffset 61 | ) 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/Symbol/SymbolFlags.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SymbolFlags.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/08. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct SymbolFlags: BitFlags { 12 | public typealias RawValue = Int32 13 | 14 | public let rawValue: RawValue 15 | 16 | public var stab: Stab? { 17 | guard rawValue & N_STAB != 0 else { return nil } 18 | return .init(rawValue: rawValue) 19 | } 20 | 21 | public var type: SymbolType? { 22 | let rawValue = (rawValue & N_TYPE) 23 | return .init(rawValue: rawValue) 24 | } 25 | 26 | public init(rawValue: RawValue) { 27 | self.rawValue = rawValue 28 | } 29 | } 30 | 31 | extension SymbolFlags { 32 | /// N_PEXT 33 | public static let pext = SymbolFlags( 34 | rawValue: Bit.pext.rawValue 35 | ) 36 | /// N_EXT 37 | public static let ext = SymbolFlags( 38 | rawValue: Bit.ext.rawValue 39 | ) 40 | } 41 | 42 | extension SymbolFlags { 43 | public enum Bit: Sendable, CaseIterable { 44 | /// N_PEXT 45 | /// private external symbol bit 46 | case pext 47 | /// N_EXT 48 | /// external symbol bit, set for external symbols 49 | case ext 50 | } 51 | } 52 | 53 | extension SymbolFlags.Bit: RawRepresentable { 54 | public typealias RawValue = Int32 55 | 56 | public init?(rawValue: RawValue) { 57 | switch rawValue { 58 | case N_PEXT: self = .pext 59 | case N_EXT: self = .ext 60 | default: return nil 61 | } 62 | } 63 | 64 | public var rawValue: RawValue { 65 | switch self { 66 | case .pext: N_PEXT 67 | case .ext: N_EXT 68 | } 69 | } 70 | } 71 | 72 | extension SymbolFlags.Bit: CustomStringConvertible { 73 | public var description: String { 74 | switch self { 75 | case .pext: "N_PEXT" 76 | case .ext: "N_EXT" 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Sources/MachOKit/LoadCommand/Model/Version.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Version.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/11/29. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct Version: Equatable, Sendable { 12 | public let major: Int 13 | public let minor: Int 14 | public let patch: Int 15 | } 16 | 17 | extension Version { 18 | init(_ version: UInt32) { 19 | self.init( 20 | major: Int((version & 0xFFFF0000) >> 16), 21 | minor: Int((version & 0x0000FF00) >> 8), 22 | patch: Int(version & 0x000000FF) 23 | ) 24 | } 25 | } 26 | 27 | extension Version: CustomStringConvertible { 28 | public var description: String { 29 | "\(major).\(minor).\(patch)" 30 | } 31 | } 32 | 33 | extension Version: Comparable { 34 | public static func < (lhs: Version, rhs: Version) -> Bool { 35 | if lhs.major != rhs.major { return lhs.major < rhs.major } 36 | if lhs.minor != rhs.minor { return lhs.minor < rhs.minor } 37 | if lhs.patch != rhs.patch { return lhs.patch < rhs.patch } 38 | return false 39 | } 40 | } 41 | 42 | public struct SourceVersion { 43 | public let a: Int 44 | public let b: Int 45 | public let c: Int 46 | public let d: Int 47 | public let e: Int 48 | } 49 | 50 | extension SourceVersion { 51 | init(_ version: UInt64) { 52 | self.init( 53 | a: Int((version >> 40) & 0xFFFFFF), 54 | b: Int((version >> 30) & 0x3FF), 55 | c: Int((version >> 20) & 0x3FF), 56 | d: Int((version >> 10) & 0x3FF), 57 | e: Int((version >> 0) & 0x3FF) 58 | ) 59 | } 60 | } 61 | 62 | extension SourceVersion: CustomStringConvertible { 63 | public var description: String { 64 | var components = [a, b, c, d, e] 65 | if e == 0 { _ = components.popLast() } 66 | if e == 0 && d == 0 { _ = components.popLast() } 67 | return components.map(String.init).joined(separator: ".") 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Sources/MachOKit/Util/TrieTree/DataTrieTree.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DataTrieTree.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/07/05 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct DataTrieTree: TrieTreeProtocol, Sendable { 12 | public let data: Data 13 | 14 | public var size: Int { data.count } 15 | 16 | @_spi(Support) 17 | public init(data: Data) { 18 | self.data = data 19 | } 20 | } 21 | 22 | extension DataTrieTree { 23 | public func element(atOffset offset: Int) -> TrieNode? { 24 | var nextOffset: Int = offset 25 | 26 | return data.withUnsafeBytes { 27 | guard let basePointer = $0.baseAddress else { return nil } 28 | 29 | return .readNext( 30 | basePointer: basePointer.assumingMemoryBound(to: UInt8.self), 31 | trieSize: data.count, 32 | nextOffset: &nextOffset 33 | ) 34 | } 35 | } 36 | } 37 | 38 | extension DataTrieTree: Sequence { 39 | public typealias Element = TrieNode 40 | 41 | public func makeIterator() -> Iterator { 42 | .init(data: data) 43 | } 44 | } 45 | 46 | extension DataTrieTree { 47 | public struct Iterator: IteratorProtocol { 48 | private let data: Data 49 | internal var nextOffset: Int = 0 50 | 51 | @_spi(Support) 52 | public init(data: Data) { 53 | self.data = data 54 | } 55 | 56 | public mutating func next() -> Element? { 57 | guard nextOffset < data.count else { return nil } 58 | 59 | return data.withUnsafeBytes { 60 | guard let basePointer = $0.baseAddress else { return nil } 61 | 62 | return .readNext( 63 | basePointer: basePointer.assumingMemoryBound(to: UInt8.self), 64 | trieSize: data.count, 65 | nextOffset: &nextOffset 66 | ) 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DataInCodeEntry.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DataInCodeEntry.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/01/07. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct DataInCodeEntry: LayoutWrapper, Sendable { 12 | public typealias Layout = data_in_code_entry 13 | 14 | public var layout: Layout 15 | } 16 | 17 | extension DataInCodeEntry { 18 | public var kind: Kind? { 19 | .init(rawValue: numericCast(layout.kind)) 20 | } 21 | } 22 | 23 | extension DataInCodeEntry { 24 | public enum Kind: Sendable { 25 | case data 26 | case jumpTable8 27 | case jumpTable16 28 | case jumpTable32 29 | case absJumpTable32 30 | } 31 | } 32 | 33 | extension DataInCodeEntry.Kind: RawRepresentable { 34 | public typealias RawValue = Int32 35 | 36 | public var rawValue: RawValue { 37 | switch self { 38 | case .data: DICE_KIND_DATA 39 | case .jumpTable8: DICE_KIND_JUMP_TABLE8 40 | case .jumpTable16: DICE_KIND_JUMP_TABLE16 41 | case .jumpTable32: DICE_KIND_JUMP_TABLE32 42 | case .absJumpTable32: DICE_KIND_ABS_JUMP_TABLE32 43 | } 44 | } 45 | 46 | public init?(rawValue: RawValue) { 47 | switch rawValue { 48 | case DICE_KIND_DATA: self = .data 49 | case DICE_KIND_JUMP_TABLE8: self = .jumpTable8 50 | case DICE_KIND_JUMP_TABLE16: self = .jumpTable16 51 | case DICE_KIND_JUMP_TABLE32: self = .jumpTable32 52 | case DICE_KIND_ABS_JUMP_TABLE32: self = .absJumpTable32 53 | default: return nil 54 | } 55 | } 56 | } 57 | 58 | extension DataInCodeEntry.Kind: CustomStringConvertible { 59 | public var description: String { 60 | switch self { 61 | case .data: "DICE_KIND_DATA" 62 | case .jumpTable8: "DICE_KIND_JUMP_TABLE8" 63 | case .jumpTable16: "DICE_KIND_JUMP_TABLE16" 64 | case .jumpTable32: "DICE_KIND_JUMP_TABLE32" 65 | case .absJumpTable32: "DICE_KIND_ABS_JUMP_TABLE32" 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldCache/DyldCacheFunctionVariantInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldCacheFunctionVariantInfo.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/05/12 6 | // 7 | // 8 | 9 | import Foundation 10 | import MachOKitC 11 | 12 | public struct DyldCacheFunctionVariantInfo: LayoutWrapper, Sendable { 13 | public typealias Layout = dyld_cache_function_variant_info 14 | 15 | public var layout: Layout 16 | public var address: Int 17 | } 18 | 19 | extension DyldCacheFunctionVariantInfo { 20 | public func entries(in cache: DyldCacheLoaded) -> MemorySequence? { 21 | guard let basePointer = UnsafeRawPointer(bitPattern: address) else { 22 | return nil 23 | } 24 | return .init( 25 | basePointer: basePointer 26 | .advanced(by: layoutOffset(of: \.entries)) 27 | .assumingMemoryBound(to: DyldCacheFunctionVariantEntry.self), 28 | numberOfElements: numericCast(layout.count) 29 | ) 30 | } 31 | 32 | public func entries(in cache: DyldCache) -> DataSequence? { 33 | _entries(in: cache) 34 | } 35 | 36 | public func entries(in cache: FullDyldCache) -> DataSequence? { 37 | _entries(in: cache) 38 | } 39 | } 40 | 41 | extension DyldCacheFunctionVariantInfo { 42 | internal func _entries( 43 | in cache: Cache 44 | ) -> DataSequence? { 45 | guard let offset = cache.fileOffset(of: numericCast(address)) else { 46 | return nil 47 | } 48 | return cache.fileHandle.readDataSequence( 49 | offset: offset + numericCast(layoutOffset(of: \.entries)), 50 | numberOfElements: numericCast(layout.count) 51 | ) 52 | } 53 | } 54 | 55 | extension DyldCacheFunctionVariantInfo { 56 | public var size: Int { 57 | layoutSize + DyldCacheFunctionVariantEntry.layoutSize * numericCast(layout.count) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldCache/DyldCachePrewarming.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldCachePrewarming.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/05/13 6 | // 7 | // 8 | 9 | import Foundation 10 | import MachOKitC 11 | 12 | public struct DyldCachePrewarming: LayoutWrapper, Sendable { 13 | public typealias Layout = dyld_prewarming_header 14 | 15 | public var layout: Layout 16 | /// offset from start address of main cache 17 | public let offset: Int 18 | } 19 | 20 | extension DyldCachePrewarming { 21 | public func entries(in cache: DyldCacheLoaded) -> MemorySequence? { 22 | .init( 23 | basePointer: cache.ptr 24 | .advanced(by: offset) 25 | .advanced(by: layoutOffset(of: \.entries)) 26 | .assumingMemoryBound(to: DyldCachePrewarmingEntry.self), 27 | numberOfElements: numericCast(layout.count) 28 | ) 29 | } 30 | 31 | public func entries(in cache: DyldCache) -> DataSequence? { 32 | _entries(in: cache) 33 | } 34 | 35 | public func entries(in cache: FullDyldCache) -> DataSequence? { 36 | _entries(in: cache) 37 | } 38 | } 39 | 40 | extension DyldCachePrewarming { 41 | internal func _entries( 42 | in cache: Cache 43 | ) -> DataSequence? { 44 | let offset = offset + layoutOffset(of: \.entries) 45 | let sharedRegionStart = cache.mainCacheHeader.sharedRegionStart 46 | guard let resolvedOffset = cache.fileOffset( 47 | of: sharedRegionStart + numericCast(offset) 48 | ) else { 49 | return nil 50 | } 51 | return cache.fileHandle.readDataSequence( 52 | offset: resolvedOffset, 53 | numberOfElements: numericCast(layout.count) 54 | ) 55 | } 56 | } 57 | 58 | extension DyldCachePrewarming { 59 | public var size: Int { 60 | layoutSize + DyldCachePrewarmingEntry.layoutSize * numericCast(layout.count) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Sources/MachOKit/LoadCommand/BuildVersionCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BuildVersionCommand.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/11/29. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct BuildVersionCommand: LoadCommandWrapper { 12 | public typealias Layout = build_version_command 13 | 14 | public var layout: Layout 15 | public var offset: Int // offset from mach header trailing 16 | 17 | init(_ layout: Layout, offset: Int) { 18 | self.layout = layout 19 | self.offset = offset 20 | } 21 | } 22 | 23 | extension BuildVersionCommand { 24 | public var platform: Platform { 25 | .init(rawValue: layout.platform) ?? .unknown 26 | } 27 | 28 | public var minos: Version { 29 | .init(layout.minos) 30 | } 31 | 32 | public var sdk: Version { 33 | .init(layout.sdk) 34 | } 35 | } 36 | 37 | extension BuildVersionCommand { 38 | public func tools( 39 | cmdsStart: UnsafeRawPointer 40 | ) -> MemorySequence { 41 | let base = cmdsStart 42 | .advanced(by: offset) 43 | .advanced(by: layoutSize) 44 | .assumingMemoryBound(to: BuildToolVersion.self) 45 | return .init( 46 | basePointer: base, 47 | numberOfElements: Int(layout.ntools) 48 | ) 49 | } 50 | } 51 | 52 | extension BuildVersionCommand { 53 | public func tools( 54 | in machO: MachOFile 55 | ) -> DataSequence { 56 | let offset = machO.cmdsStartOffset + offset + layoutSize 57 | 58 | return machO.fileHandle.readDataSequence( 59 | offset: numericCast(offset), 60 | numberOfElements: numericCast(layout.ntools), 61 | swapHandler: { data in 62 | guard machO.isSwapped else { return } 63 | data.withUnsafeBytes { 64 | guard let baseAddress = $0.baseAddress else { return } 65 | let ptr = UnsafeMutableRawPointer(mutating: baseAddress) 66 | .assumingMemoryBound(to: build_tool_version.self) 67 | swap_build_tool_version(ptr, layout.ntools, NXHostByteOrder()) 68 | } 69 | } 70 | ) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Sources/MachOKit/MachOFile+RebaseOperations.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MachOFile+RebaseOperations.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/09. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | extension MachOFile { 12 | public struct RebaseOperations: Sequence { 13 | public let data: Data 14 | public let rebaseOffset: Int 15 | public let rebaseSize: Int 16 | 17 | public func makeIterator() -> Iterator { 18 | .init(data: data) 19 | } 20 | } 21 | } 22 | 23 | extension MachOFile.RebaseOperations { 24 | init( 25 | machO: MachOFile, 26 | rebaseOffset: Int, 27 | rebaseSize: Int 28 | ) { 29 | let offset = machO.headerStartOffset + rebaseOffset 30 | let data = try! machO.fileHandle.readData( 31 | offset: offset, 32 | length: rebaseSize 33 | ) 34 | 35 | self.init( 36 | data: data, 37 | rebaseOffset: rebaseOffset, 38 | rebaseSize: rebaseSize 39 | ) 40 | } 41 | 42 | init( 43 | machO: MachOFile, 44 | info: dyld_info_command 45 | ) { 46 | self.init( 47 | machO: machO, 48 | rebaseOffset: Int(info.rebase_off), 49 | rebaseSize: Int(info.rebase_size) 50 | ) 51 | } 52 | } 53 | 54 | extension MachOFile.RebaseOperations { 55 | public struct Iterator: IteratorProtocol { 56 | public typealias Element = RebaseOperation 57 | 58 | private let data: Data 59 | private var nextOffset: Int = 0 60 | private var done = false 61 | 62 | init(data: Data) { 63 | self.data = data 64 | } 65 | 66 | public mutating func next() -> Element? { 67 | guard !done, nextOffset < data.count else { return nil } 68 | 69 | return data.withUnsafeBytes { 70 | guard let basePointer = $0.baseAddress else { return nil } 71 | 72 | return RebaseOperation.readNext( 73 | basePointer: basePointer.assumingMemoryBound(to: UInt8.self), 74 | rebaseSize: data.count, 75 | nextOffset: &nextOffset, 76 | done: &done 77 | ) 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/AotCache/AotCacheCodeFragment.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AotCacheCodeFragment.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/12/15 6 | // 7 | // 8 | 9 | import MachOKitC 10 | import Foundation 11 | 12 | public struct AotCacheCodeFragment: LayoutWrapper { 13 | public typealias Layout = aot_cache_code_fragment_metadata 14 | 15 | public var layout: Layout 16 | public let offset: Int 17 | } 18 | 19 | extension AotCacheCodeFragment { 20 | public var type: AotCodeFragmentType { 21 | .init(rawValue: layout.type)! 22 | } 23 | } 24 | 25 | extension AotCacheCodeFragment { 26 | public func imagePath( 27 | x86DyldCache cache: DyldCache 28 | ) -> String? { 29 | _imagePath(x86DyldCache: cache) 30 | } 31 | 32 | public func imagePath( 33 | x86DyldCache cache: FullDyldCache 34 | ) -> String? { 35 | _imagePath(x86DyldCache: cache) 36 | } 37 | 38 | private func _imagePath( 39 | x86DyldCache cache: Cache 40 | ) -> String? { 41 | if type == .runtime { return "RuntimeRoutines" } 42 | let address = cache.mainCacheHeader.sharedRegionStart + numericCast(layout.image_path_offset) 43 | guard let fileOffset = cache.fileOffset( 44 | of: address 45 | ) else { return nil } 46 | 47 | return cache.fileHandle.readString( 48 | offset: fileOffset 49 | ) 50 | } 51 | } 52 | 53 | extension AotCacheCodeFragment { 54 | public func instructionMap( 55 | in cache: AotCache 56 | ) -> AotInstructionMap? { 57 | guard layout.instruction_map_size > 0 else { return nil } 58 | let offset = offset + layoutSize + numericCast(layout.branch_data_size) 59 | return .init( 60 | header: try! cache.fileHandle.read(offset: offset), 61 | offset: offset 62 | ) 63 | } 64 | } 65 | 66 | extension AotCacheCodeFragment { 67 | public func branchData( 68 | in cache: AotCache 69 | ) -> AotBranchData? { 70 | guard layout.branch_data_size > 0 else { return nil } 71 | let offset = offset + layoutSize 72 | return .init( 73 | header: try! cache.fileHandle.read(offset: offset), 74 | offset: offset 75 | ) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Sources/MachOKit/MachOFile+LoadCommands.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MachOFile+LoadCommands.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/04. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | extension MachOFile { 12 | public struct LoadCommands: Sequence { 13 | public let data: Data 14 | public let numberOfCommands: Int 15 | public let isSwapped: Bool 16 | 17 | public func makeIterator() -> Iterator { 18 | Iterator( 19 | data: data, 20 | numberOfCommands: numberOfCommands, 21 | isSwapped: isSwapped 22 | ) 23 | } 24 | } 25 | } 26 | 27 | extension MachOFile.LoadCommands { 28 | public struct Iterator: IteratorProtocol { 29 | public typealias Element = LoadCommand 30 | 31 | public let data: Data 32 | public let numberOfCommands: Int 33 | public let isSwapped: Bool 34 | 35 | private var nextOffset: Int = 0 36 | private var nextIndex: Int = 0 37 | 38 | public init( 39 | data: Data, 40 | numberOfCommands: Int, 41 | isSwapped: Bool 42 | ) { 43 | self.data = data 44 | self.numberOfCommands = numberOfCommands 45 | self.isSwapped = isSwapped 46 | } 47 | 48 | public mutating func next() -> Element? { 49 | guard nextOffset < data.count else { 50 | return nil 51 | } 52 | guard nextIndex < numberOfCommands else { 53 | return nil 54 | } 55 | 56 | return data.withUnsafeBytes { 57 | guard let baseAddress = $0.baseAddress else { return nil } 58 | 59 | let ptr = UnsafeMutableRawPointer(mutating: baseAddress.advanced(by: nextOffset)) 60 | .assumingMemoryBound(to: load_command.self) 61 | 62 | var next = LoadCommand.convert(UnsafePointer(ptr), offset: nextOffset) 63 | 64 | if isSwapped { 65 | next = next?.swapped() 66 | } 67 | 68 | nextOffset += Int(isSwapped ? ptr.pointee.cmdsize.byteSwapped : ptr.pointee.cmdsize) 69 | nextIndex += 1 70 | 71 | return next 72 | } 73 | } 74 | } 75 | } 76 | 77 | extension MachOFile.LoadCommands: LoadCommandsProtocol {} 78 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldCache/SlideInfo/DyldCacheSlideInfo1.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldCacheSlideInfo1.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/07/23 6 | // 7 | // 8 | 9 | import Foundation 10 | import MachOKitC 11 | 12 | public struct DyldCacheSlideInfo1: LayoutWrapper, Sendable { 13 | public typealias Layout = dyld_cache_slide_info 14 | 15 | public var layout: Layout 16 | public var offset: Int 17 | } 18 | 19 | extension DyldCacheSlideInfo1 { 20 | public struct Entry: LayoutWrapper { 21 | public typealias Layout = dyld_cache_slide_info_entry 22 | 23 | public var layout: Layout 24 | } 25 | } 26 | 27 | extension DyldCacheSlideInfo1 { 28 | public var numberOfTableContents: Int { 29 | numericCast(layout.toc_count) 30 | } 31 | 32 | public func toc(in cache: DyldCache) -> DataSequence? { 33 | _toc(in: cache) 34 | } 35 | 36 | public func toc(in cache: FullDyldCache) -> DataSequence? { 37 | _toc(in: cache) 38 | } 39 | } 40 | 41 | extension DyldCacheSlideInfo1 { 42 | internal func _toc( 43 | in cache: Cache 44 | ) -> DataSequence? { 45 | guard layout.toc_offset > 0 else { return nil } 46 | return cache.fileHandle.readDataSequence( 47 | offset: numericCast(offset) + numericCast(layout.toc_offset), 48 | numberOfElements: numberOfTableContents 49 | ) 50 | } 51 | } 52 | 53 | extension DyldCacheSlideInfo1 { 54 | public var numberOfEntries: Int { 55 | numericCast(layout.entries_count) 56 | } 57 | 58 | public func entries(in cache: DyldCache) -> DataSequence? { 59 | _entries(in: cache) 60 | } 61 | 62 | public func entries(in cache: FullDyldCache) -> DataSequence? { 63 | _entries(in: cache) 64 | } 65 | } 66 | 67 | extension DyldCacheSlideInfo1 { 68 | internal func _entries( 69 | in cache: Cache 70 | ) -> DataSequence? { 71 | precondition(layout.entries_size == Entry.layoutSize) 72 | guard layout.entries_offset > 0 else { return nil } 73 | return cache.fileHandle.readDataSequence( 74 | offset: numericCast(offset) + numericCast(layout.entries_offset), 75 | numberOfElements: numberOfEntries 76 | ) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldCache/Loader/SectionLocations.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SectionLocations.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2024/11/16 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct SectionLocations: LayoutWrapper, Sendable { 12 | public typealias Layout = section_locations 13 | 14 | public var layout: Layout 15 | } 16 | 17 | extension SectionLocations { 18 | // [dyld implementation](https://github.com/apple-oss-distributions/dyld/blob/65bbeed63cec73f313b1d636e63f243964725a9d/include/mach-o/dyld_priv.h#L62) 19 | public enum SectionKind: Int, Sendable, CaseIterable { 20 | // TEXT: 21 | case text_swift5_protos 22 | case text_swift5_proto 23 | case text_swift5_types 24 | case text_swift5_replace 25 | case text_swift5_replace2 26 | case text_swift5_ac_funcs 27 | 28 | // DATA*: 29 | case objc_image_info 30 | case data_sel_refs 31 | case data_msg_refs 32 | case data_class_refs 33 | case data_super_refs 34 | case data_protocol_refs 35 | case data_class_list 36 | case data_non_lazy_class_list 37 | case data_stub_list 38 | case data_category_list 39 | case data_category_list2 40 | case data_non_lazy_category_list 41 | case data_protocol_list 42 | case data_objc_fork_ok 43 | case data_raw_isa 44 | 45 | // ~~ version 1 ~~ 46 | } 47 | } 48 | 49 | extension SectionLocations { 50 | public struct Section: Sendable { 51 | public let offset: Int 52 | public let size: Int 53 | public let kind: SectionKind 54 | } 55 | } 56 | 57 | extension SectionLocations { 58 | public func section(for kind: SectionKind) -> Section { 59 | var offsets = layout.offsets 60 | var sizes = layout.sizes 61 | let offset = withUnsafePointer(to: &offsets) { 62 | UnsafeRawPointer($0) 63 | .assumingMemoryBound(to: UInt64.self) 64 | .advanced(by: kind.rawValue).pointee 65 | } 66 | let size = withUnsafePointer(to: &sizes) { 67 | UnsafeRawPointer($0) 68 | .assumingMemoryBound(to: UInt64.self) 69 | .advanced(by: kind.rawValue).pointee 70 | } 71 | return .init( 72 | offset: numericCast(offset), 73 | size: numericCast(size), 74 | kind: kind 75 | ) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Sources/MachOKit/MachOImage+RebaseOperations.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MachO+RebaseOperations.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/03. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | extension MachOImage { 12 | public struct RebaseOperations: Sequence { 13 | public let basePointer: UnsafePointer 14 | public let rebaseSize: Int 15 | 16 | public func makeIterator() -> Iterator { 17 | .init(basePointer: basePointer, rebaseSize: rebaseSize) 18 | } 19 | } 20 | } 21 | 22 | extension MachOImage.RebaseOperations { 23 | init( 24 | ptr: UnsafeRawPointer, 25 | text: SegmentCommand64, 26 | linkedit: SegmentCommand64, 27 | info: dyld_info_command 28 | ) { 29 | let fileSlide = Int(linkedit.vmaddr) - Int(text.vmaddr) - Int(linkedit.fileoff) 30 | let ptr = ptr 31 | .advanced(by: Int(info.rebase_off)) 32 | .advanced(by: Int(fileSlide)) 33 | .assumingMemoryBound(to: UInt8.self) 34 | 35 | self.init(basePointer: ptr, rebaseSize: Int(info.rebase_size)) 36 | } 37 | 38 | init( 39 | ptr: UnsafeRawPointer, 40 | text: SegmentCommand, 41 | linkedit: SegmentCommand, 42 | info: dyld_info_command 43 | ) { 44 | let fileSlide = Int(linkedit.vmaddr) - Int(text.vmaddr) - Int(linkedit.fileoff) 45 | let ptr = ptr 46 | .advanced(by: Int(info.rebase_off)) 47 | .advanced(by: Int(fileSlide)) 48 | .assumingMemoryBound(to: UInt8.self) 49 | 50 | self.init(basePointer: ptr, rebaseSize: Int(info.rebase_size)) 51 | } 52 | } 53 | 54 | extension MachOImage.RebaseOperations { 55 | public struct Iterator: IteratorProtocol { 56 | public typealias Element = RebaseOperation 57 | 58 | private let basePointer: UnsafePointer 59 | private let rebaseSize: Int 60 | 61 | private var nextOffset: Int = 0 62 | private var done = false 63 | 64 | init(basePointer: UnsafePointer, rebaseSize: Int) { 65 | self.basePointer = basePointer 66 | self.rebaseSize = rebaseSize 67 | } 68 | 69 | public mutating func next() -> Element? { 70 | RebaseOperation.readNext( 71 | basePointer: basePointer, 72 | rebaseSize: rebaseSize, 73 | nextOffset: &nextOffset, 74 | done: &done 75 | ) 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/Codesign/CodeDirectory/CodeSignCodeDirectory+teamID.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CodeSignCodeDirectory+teamID.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/03/11. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | extension CodeSignCodeDirectory { 12 | public func teamIdOffset(in signature: MachOFile.CodeSign) -> TeamIdOffset? { 13 | guard isSupportsScatter else { return nil } 14 | let layout: CS_CodeDirectory_TeamID = signature.fileSlice.ptr 15 | .advanced(by: offset) 16 | .advanced(by: layoutSize) 17 | .advanced(by: ScatterOffset.layoutSize) 18 | .assumingMemoryBound(to: CS_CodeDirectory_TeamID.self) 19 | .pointee 20 | return .init( 21 | layout: signature.isSwapped ? layout.swapped : layout 22 | ) 23 | } 24 | 25 | public func teamId(in signature: MachOFile.CodeSign) -> String? { 26 | guard let teamIdOffset = teamIdOffset(in: signature), 27 | teamIdOffset.teamOffset != 0 else { 28 | return nil 29 | } 30 | let ptr = signature.fileSlice.ptr 31 | .advanced(by: offset) 32 | .advanced(by: Int(teamIdOffset.teamOffset)) 33 | .assumingMemoryBound(to: CChar.self) 34 | return String(cString: ptr) 35 | } 36 | } 37 | 38 | extension CodeSignCodeDirectory { 39 | public func teamIdOffset(in signature: MachOImage.CodeSign) -> TeamIdOffset? { 40 | guard isSupportsScatter else { 41 | return nil 42 | } 43 | let layout: CS_CodeDirectory_TeamID? = signature.basePointer 44 | .advanced(by: offset) 45 | .advanced(by: layoutSize) 46 | .advanced(by: ScatterOffset.layoutSize) 47 | .assumingMemoryBound(to: CS_CodeDirectory_TeamID.self) 48 | .pointee 49 | guard let layout else { return nil } 50 | return .init( 51 | layout: signature.isSwapped ? layout.swapped : layout 52 | ) 53 | } 54 | 55 | public func teamId(in signature: MachOImage.CodeSign) -> String? { 56 | guard let teamIdOffset = teamIdOffset(in: signature), 57 | teamIdOffset.teamOffset != 0 else { 58 | return nil 59 | } 60 | let ptr = signature.basePointer 61 | .advanced(by: offset) 62 | .advanced(by: Int(teamIdOffset.teamOffset)) 63 | .assumingMemoryBound(to: CChar.self) 64 | return String(cString: ptr) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Sources/MachOKit/AotCache.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AotCache.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/01/29 6 | // 7 | // 8 | 9 | import Foundation 10 | #if compiler(>=6.0) || (compiler(>=5.10) && hasFeature(AccessLevelOnImport)) 11 | internal import FileIO 12 | #else 13 | @_implementationOnly import FileIO 14 | #endif 15 | 16 | public struct AotCache { 17 | typealias File = MemoryMappedFile 18 | 19 | /// URL of loaded dyld cache file 20 | public let url: URL 21 | let fileHandle: File 22 | 23 | // public var headerSize: Int { 24 | // header.headerSize 25 | // } 26 | 27 | /// Header for dyld cache 28 | public let header: AotCacheHeader 29 | 30 | public init(url: URL) throws { 31 | self.url = url 32 | let fileHandle = try File.open( 33 | url: url, 34 | isWritable: false 35 | ) 36 | self.fileHandle = fileHandle 37 | 38 | // read header 39 | self.header = try! fileHandle.read( 40 | offset: 0 41 | ) 42 | 43 | // check magic of header 44 | guard header.magic == "AotCache" else { 45 | throw MachOKitError.invalidMagic 46 | } 47 | } 48 | } 49 | 50 | extension AotCache { 51 | public var codeSign: MachOFile.CodeSign? { 52 | .init( 53 | fileSlice: try! fileHandle.fileSlice( 54 | offset: numericCast(header.code_signature_offset), 55 | length: numericCast(header.code_signature_size) 56 | ) 57 | ) 58 | } 59 | } 60 | 61 | extension AotCache { 62 | /// Sequence of mapping infos 63 | public var mappingInfos: DataSequence { 64 | fileHandle.readDataSequence( 65 | offset: numericCast(header.layoutSize), 66 | numberOfElements: 3 67 | ) 68 | } 69 | } 70 | 71 | extension AotCache { 72 | /// Sequence of code fragments 73 | public var codeFragments: CodeFragments { 74 | let mapping = mappingInfos[0] 75 | 76 | let offset = header.headerSize 77 | let size = numericCast(mapping.size) - offset 78 | 79 | let fileSlice = try! fileHandle.fileSlice( 80 | offset: offset, 81 | length: numericCast(mapping.size) - offset 82 | ) 83 | return .init( 84 | fileSlice: fileSlice, 85 | offset: offset, 86 | size: size, 87 | numberOfEntries: numericCast(header.num_code_fragments) 88 | ) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Sources/MachOKit/MachOImage+BindOperations.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MachO+BindOperations.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/03. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | extension MachOImage { 12 | public struct BindOperations: Sequence { 13 | public let basePointer: UnsafePointer 14 | public let bindSize: Int 15 | 16 | public func makeIterator() -> Iterator { 17 | .init(basePointer: basePointer, bindSize: bindSize) 18 | } 19 | } 20 | } 21 | 22 | extension MachOImage.BindOperations { 23 | init( 24 | ptr: UnsafeRawPointer, 25 | text: SegmentCommand64, 26 | linkedit: SegmentCommand64, 27 | info: dyld_info_command, 28 | kind: BindOperationsKind = .normal 29 | ) { 30 | let fileSlide = Int(linkedit.vmaddr) - Int(text.vmaddr) - Int(linkedit.fileoff) 31 | let ptr = ptr 32 | .advanced(by: Int(kind.bindOffset(of: info))) 33 | .advanced(by: Int(fileSlide)) 34 | .assumingMemoryBound(to: UInt8.self) 35 | 36 | self.init(basePointer: ptr, bindSize: Int(kind.bindSize(of: info))) 37 | } 38 | 39 | init( 40 | ptr: UnsafeRawPointer, 41 | text: SegmentCommand, 42 | linkedit: SegmentCommand, 43 | info: dyld_info_command, 44 | kind: BindOperationsKind = .normal 45 | ) { 46 | let fileSlide = Int(linkedit.vmaddr) - Int(text.vmaddr) - Int(linkedit.fileoff) 47 | let ptr = ptr 48 | .advanced(by: Int(kind.bindOffset(of: info))) 49 | .advanced(by: Int(fileSlide)) 50 | .assumingMemoryBound(to: UInt8.self) 51 | 52 | self.init(basePointer: ptr, bindSize: Int(kind.bindSize(of: info))) 53 | } 54 | } 55 | 56 | extension MachOImage.BindOperations { 57 | public struct Iterator: IteratorProtocol { 58 | public typealias Element = BindOperation 59 | 60 | private let basePointer: UnsafePointer 61 | private let bindSize: Int 62 | 63 | private var nextOffset: Int = 0 64 | 65 | init(basePointer: UnsafePointer, bindSize: Int) { 66 | self.basePointer = basePointer 67 | self.bindSize = bindSize 68 | } 69 | 70 | public mutating func next() -> Element? { 71 | BindOperation.readNext( 72 | basePointer: basePointer, 73 | bindSize: bindSize, 74 | nextOffset: &nextOffset 75 | ) 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Sources/MachOKit/LoadCommand/DylibUseCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DylibUseCommand.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2024/12/12 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct DylibUseCommand: LoadCommandWrapper { 12 | public typealias Layout = dylib_use_command 13 | 14 | public var layout: Layout 15 | public var offset: Int // offset from mach header trailing 16 | 17 | init(_ layout: Layout, offset: Int) { 18 | self.layout = layout 19 | self.offset = offset 20 | } 21 | } 22 | 23 | extension DylibUseCommand { 24 | public var flags: DylibUseFlags { 25 | .init(rawValue: layout.flags) 26 | } 27 | } 28 | 29 | extension DylibUseCommand { 30 | public func dylib(cmdsStart: UnsafeRawPointer) -> Dylib { 31 | let ptr = cmdsStart 32 | .advanced(by: offset) 33 | .advanced(by: Int(layout.nameoff)) 34 | .assumingMemoryBound(to: CChar.self) 35 | 36 | return .init( 37 | name: String(cString: ptr), 38 | timestamp: Date(timeIntervalSince1970: TimeInterval(layout.marker)), 39 | currentVersion: .init(layout.current_version), 40 | compatibilityVersion: .init(layout.compat_version) 41 | ) 42 | } 43 | } 44 | 45 | extension DylibUseCommand { 46 | public func dylib(in machO: MachOFile) -> Dylib { 47 | let offset = machO.cmdsStartOffset + offset + Int(layout.nameoff) 48 | // swap is not needed 49 | let string = machO.fileHandle.readString( 50 | offset: numericCast(offset), 51 | size: Int(layout.cmdsize) - layoutSize 52 | ) ?? "" 53 | 54 | return .init( 55 | name: string, 56 | timestamp: Date(timeIntervalSince1970: TimeInterval(layout.marker)), 57 | currentVersion: .init(layout.current_version), 58 | compatibilityVersion: .init(layout.compat_version) 59 | ) 60 | } 61 | } 62 | 63 | public func swap_dylib_use_command( 64 | _ dl: UnsafeMutablePointer!, 65 | _ target_byte_sex: NXByteOrder 66 | ) { 67 | dl.pointee.cmd = dl.pointee.cmd.byteSwapped 68 | dl.pointee.cmdsize = dl.pointee.cmdsize.byteSwapped 69 | dl.pointee.nameoff = dl.pointee.nameoff.byteSwapped 70 | dl.pointee.marker = dl.pointee.marker.byteSwapped 71 | dl.pointee.current_version = dl.pointee.current_version.byteSwapped 72 | dl.pointee.compat_version = dl.pointee.compat_version.byteSwapped 73 | dl.pointee.flags = dl.pointee.flags.byteSwapped 74 | } 75 | -------------------------------------------------------------------------------- /Sources/MachOKit/LoadCommand/Model/DylibUseFlags.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DylibUseFlags.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2024/12/12 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct DylibUseFlags: BitFlags { 12 | public typealias RawValue = UInt32 13 | 14 | public let rawValue: RawValue 15 | 16 | public init(rawValue: RawValue) { 17 | self.rawValue = rawValue 18 | } 19 | } 20 | 21 | extension DylibUseFlags { 22 | /// DYLIB_USE_WEAK_LINK 23 | public static let weak_link = DylibUseFlags( 24 | rawValue: Bit.weak_link.rawValue 25 | ) 26 | /// DYLIB_USE_REEXPORT 27 | public static let reexport = DylibUseFlags( 28 | rawValue: Bit.reexport.rawValue 29 | ) 30 | /// DYLIB_USE_UPWARD 31 | public static let upward = DylibUseFlags( 32 | rawValue: Bit.upward.rawValue 33 | ) 34 | /// DYLIB_USE_DELAYED_INIT 35 | public static let delayed_init = DylibUseFlags( 36 | rawValue: Bit.delayed_init.rawValue 37 | ) 38 | } 39 | 40 | extension DylibUseFlags { 41 | public enum Bit: Sendable, CaseIterable { 42 | /// DYLIB_USE_WEAK_LINK 43 | case weak_link 44 | /// DYLIB_USE_REEXPORT 45 | case reexport 46 | /// DYLIB_USE_UPWARD 47 | case upward 48 | /// DYLIB_USE_DELAYED_INIT 49 | case delayed_init 50 | } 51 | } 52 | 53 | extension DylibUseFlags.Bit: RawRepresentable { 54 | public typealias RawValue = UInt32 55 | 56 | public init?(rawValue: RawValue) { 57 | switch rawValue { 58 | case RawValue(DYLIB_USE_WEAK_LINK): self = .weak_link 59 | case RawValue(DYLIB_USE_REEXPORT): self = .reexport 60 | case RawValue(DYLIB_USE_UPWARD): self = .upward 61 | case RawValue(DYLIB_USE_DELAYED_INIT): self = .delayed_init 62 | default: return nil 63 | } 64 | } 65 | 66 | public var rawValue: RawValue { 67 | switch self { 68 | case .weak_link: RawValue(DYLIB_USE_WEAK_LINK) 69 | case .reexport: RawValue(DYLIB_USE_REEXPORT) 70 | case .upward: RawValue(DYLIB_USE_UPWARD) 71 | case .delayed_init: RawValue(DYLIB_USE_DELAYED_INIT) 72 | } 73 | } 74 | } 75 | 76 | extension DylibUseFlags.Bit: CustomStringConvertible { 77 | public var description: String { 78 | switch self { 79 | case .weak_link: "DYLIB_USE_WEAK_LINK" 80 | case .reexport: "DYLIB_USE_REEXPORT" 81 | case .upward: "DYLIB_USE_UPWARD" 82 | case .delayed_init: "DYLIB_USE_DELAYED_INIT" 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/Symbol/SymbolReferenceFlag.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SymbolReferenceFlag.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/08. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public enum SymbolReferenceFlag: Sendable { 12 | /// REFERENCE_FLAG_UNDEFINED_NON_LAZY 13 | case undefined_non_lazy 14 | /// REFERENCE_FLAG_UNDEFINED_LAZY 15 | case undefined_lazy 16 | /// REFERENCE_FLAG_DEFINED 17 | case defined 18 | /// REFERENCE_FLAG_PRIVATE_DEFINED 19 | case private_defined 20 | /// REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY 21 | case private_undefined_non_lazy 22 | /// REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY 23 | case private_undefined_lazy 24 | } 25 | 26 | extension SymbolReferenceFlag: RawRepresentable { 27 | public typealias RawValue = Int32 28 | 29 | public init?(rawValue: RawValue) { 30 | switch rawValue { 31 | case RawValue(REFERENCE_FLAG_UNDEFINED_NON_LAZY): self = .undefined_non_lazy 32 | case RawValue(REFERENCE_FLAG_UNDEFINED_LAZY): self = .undefined_lazy 33 | case RawValue(REFERENCE_FLAG_DEFINED): self = .defined 34 | case RawValue(REFERENCE_FLAG_PRIVATE_DEFINED): self = .private_defined 35 | case RawValue(REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY): self = .private_undefined_non_lazy 36 | case RawValue(REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY): self = .private_undefined_lazy 37 | default: return nil 38 | } 39 | } 40 | 41 | public var rawValue: RawValue { 42 | switch self { 43 | case .undefined_non_lazy: RawValue(REFERENCE_FLAG_UNDEFINED_NON_LAZY) 44 | case .undefined_lazy: RawValue(REFERENCE_FLAG_UNDEFINED_LAZY) 45 | case .defined: RawValue(REFERENCE_FLAG_DEFINED) 46 | case .private_defined: RawValue(REFERENCE_FLAG_PRIVATE_DEFINED) 47 | case .private_undefined_non_lazy: RawValue(REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY) 48 | case .private_undefined_lazy: RawValue(REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY) 49 | } 50 | } 51 | } 52 | 53 | extension SymbolReferenceFlag: CustomStringConvertible { 54 | public var description: String { 55 | switch self { 56 | case .undefined_non_lazy: "REFERENCE_FLAG_UNDEFINED_NON_LAZY" 57 | case .undefined_lazy: "REFERENCE_FLAG_UNDEFINED_LAZY" 58 | case .defined: "REFERENCE_FLAG_DEFINED" 59 | case .private_defined: "REFERENCE_FLAG_PRIVATE_DEFINED" 60 | case .private_undefined_non_lazy: "REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY" 61 | case .private_undefined_lazy: "REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY" 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Sources/MachOKit/Extension/UnsafePointer+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UnsafePointer+.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/03. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | extension UnsafePointer { 12 | /// (value, size) 13 | @_spi(Support) 14 | public func readULEB128() -> (UInt, Int) { 15 | var value: UInt = 0 16 | var shift: UInt = 0 17 | var offset: Int = 0 18 | 19 | var byte: UInt8 = 0 20 | 21 | repeat { 22 | byte = advanced(by: offset).pointee 23 | 24 | value += UInt(byte & 0x7F) << shift 25 | shift += 7 26 | offset += 1 27 | } while byte >= 128 28 | 29 | return (value, offset) 30 | } 31 | 32 | /// (value, size) 33 | @_spi(Support) 34 | public func readSLEB128() -> (Int, Int) { 35 | var value: Int = 0 36 | var shift: UInt = 0 37 | var offset: Int = 0 38 | 39 | var byte: UInt8 = 0 40 | 41 | repeat { 42 | byte = advanced(by: offset).pointee 43 | 44 | value += Int(byte & 0x7F) << shift 45 | shift += 7 46 | offset += 1 47 | } while byte >= 128 48 | 49 | if byte & 0x40 != 0 { 50 | value |= -(1 << shift) 51 | } 52 | 53 | return (value, offset) 54 | } 55 | } 56 | 57 | extension UnsafePointer { 58 | func readString() -> (String, Int) { 59 | let offset = Int(bitPattern: strchr(self, 0)) + 1 - Int(bitPattern: self) 60 | let string = String(cString: self) 61 | 62 | return (string, offset) 63 | } 64 | } 65 | 66 | extension UnsafePointer { 67 | func readString() -> (String, Int) { 68 | let offset = Int(bitPattern: strchr(self, 0)) + 1 - Int(bitPattern: self) 69 | let string = String(cString: self) 70 | 71 | return (string, offset) 72 | } 73 | } 74 | 75 | extension UnsafePointer where Pointee: FixedWidthInteger { 76 | func findNullTerminator() -> UnsafePointer { 77 | var ptr = self 78 | while ptr.pointee != 0 { 79 | ptr = ptr.advanced(by: 1) 80 | } 81 | return ptr 82 | } 83 | 84 | func readString( 85 | as encoding: Encoding.Type 86 | ) -> (String, Int) where Pointee == Encoding.CodeUnit { 87 | let nullTerminator = findNullTerminator() 88 | let offset = Int(bitPattern: nullTerminator) + MemoryLayout.size - Int(bitPattern: self) 89 | let string = String(decodingCString: self, as: Encoding.self) 90 | 91 | return (string, offset) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Sources/MachOKitC/include/thread_state.h: -------------------------------------------------------------------------------- 1 | // 2 | // thread_state.h 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/01/13 6 | // 7 | // 8 | 9 | #ifndef thread_state_h 10 | #define thread_state_h 11 | 12 | #include 13 | 14 | // https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/osfmk/mach/arm/_structs.h#L84 15 | struct arm_thread_state { 16 | uint32_t r[13]; /* General purpose register r0-r12 */ 17 | uint32_t sp; /* Stack pointer r13 */ 18 | uint32_t lr; /* Link register r14 */ 19 | uint32_t pc; /* Program counter r15 */ 20 | uint32_t cpsr; /* Current program status register */ 21 | }; 22 | 23 | // https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/osfmk/mach/arm/_structs.h#L101 24 | struct arm_thread_state64 { 25 | uint64_t x[29]; /* General purpose registers x0-x28 */ 26 | uint64_t fp; /* Frame pointer x29 */ 27 | uint64_t lr; /* Link register x30 */ 28 | uint64_t sp; /* Stack pointer x31 */ 29 | uint64_t pc; /* Program counter */ 30 | uint32_t cpsr; /* Current program status register */ 31 | uint32_t flags; /* Flags describing structure format */ 32 | }; 33 | 34 | // https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/osfmk/mach/i386/_structs.h#L66 35 | struct i386_thread_state { 36 | unsigned int eax; 37 | unsigned int ebx; 38 | unsigned int ecx; 39 | unsigned int edx; 40 | unsigned int edi; 41 | unsigned int esi; 42 | unsigned int ebp; 43 | unsigned int esp; 44 | unsigned int ss; 45 | unsigned int eflags; 46 | unsigned int eip; 47 | unsigned int cs; 48 | unsigned int ds; 49 | unsigned int es; 50 | unsigned int fs; 51 | unsigned int gs; 52 | }; 53 | 54 | // https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/osfmk/mach/i386/_structs.h#L738 55 | struct x86_thread_state64 { 56 | uint64_t rax; 57 | uint64_t rbx; 58 | uint64_t rcx; 59 | uint64_t rdx; 60 | uint64_t rdi; 61 | uint64_t rsi; 62 | uint64_t rbp; 63 | uint64_t rsp; 64 | uint64_t r8; 65 | uint64_t r9; 66 | uint64_t r10; 67 | uint64_t r11; 68 | uint64_t r12; 69 | uint64_t r13; 70 | uint64_t r14; 71 | uint64_t r15; 72 | uint64_t rip; 73 | uint64_t rflags; 74 | uint64_t cs; 75 | uint64_t fs; 76 | uint64_t gs; 77 | }; 78 | 79 | #endif /* thread_state_h */ 80 | -------------------------------------------------------------------------------- /Sources/MachOKit/LoadCommand/Model/Tool.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Tool.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/11/29. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public enum Tool: Sendable { 12 | /// TOOL_CLANG 13 | case clang 14 | /// TOOL_SWIFT 15 | case swift 16 | /// TOOL_LD 17 | case ld 18 | /// TOOL_LLD 19 | case lld 20 | 21 | /* values for gpu tools (1024 to 1048) */ 22 | /// TOOL_METAL 23 | case metal 24 | /// TOOL_AIRLLD 25 | case airLld 26 | /// TOOL_AIRNT 27 | case airNt 28 | /// TOOL_AIRNT_PLUGIN 29 | case airNtPlugin 30 | /// TOOL_AIRPACK 31 | case airPack 32 | /// TOOL_GPUARCHIVER 33 | case gpuArchiver 34 | /// TOOL_METAL_FRAMEWORK 35 | case metalFramework 36 | } 37 | 38 | extension Tool: RawRepresentable { 39 | public typealias RawValue = Int32 40 | 41 | public init?(rawValue: Int32) { 42 | switch rawValue { 43 | case TOOL_CLANG: self = .clang 44 | case TOOL_SWIFT: self = .swift 45 | case TOOL_LD: self = .ld 46 | case TOOL_LLD: self = .lld 47 | case TOOL_METAL: self = .metal 48 | case TOOL_AIRLLD: self = .airLld 49 | case TOOL_AIRNT: self = .airNt 50 | case TOOL_AIRNT_PLUGIN: self = .airNtPlugin 51 | case TOOL_AIRPACK: self = .airPack 52 | case TOOL_GPUARCHIVER: self = .gpuArchiver 53 | case TOOL_METAL_FRAMEWORK: self = .metalFramework 54 | default: return nil 55 | } 56 | } 57 | 58 | public var rawValue: Int32 { 59 | switch self { 60 | case .clang: TOOL_CLANG 61 | case .swift: TOOL_SWIFT 62 | case .ld: TOOL_LD 63 | case .lld: TOOL_LLD 64 | case .metal: TOOL_METAL 65 | case .airLld: TOOL_AIRLLD 66 | case .airNt: TOOL_AIRNT 67 | case .airNtPlugin: TOOL_AIRNT_PLUGIN 68 | case .airPack: TOOL_AIRPACK 69 | case .gpuArchiver: TOOL_GPUARCHIVER 70 | case .metalFramework: TOOL_METAL_FRAMEWORK 71 | } 72 | } 73 | } 74 | 75 | extension Tool: CustomStringConvertible { 76 | public var description: String { 77 | switch self { 78 | case .clang: "TOOL_CLANG" 79 | case .swift: "TOOL_SWIFT" 80 | case .ld: "TOOL_LD" 81 | case .lld: "TOOL_LLD" 82 | case .metal: "TOOL_METAL" 83 | case .airLld: "TOOL_AIRLLD" 84 | case .airNt: "TOOL_AIRNT" 85 | case .airNtPlugin: "TOOL_AIRNT_PLUGIN" 86 | case .airPack: "TOOL_AIRPACK" 87 | case .gpuArchiver: "TOOL_GPUARCHIVER" 88 | case .metalFramework: "TOOL_METAL_FRAMEWORK" 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/AotCache/BranchData/AotBranchData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AotBranchData.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/12/18 6 | // 7 | // 8 | 9 | import MachOKitC 10 | 11 | public struct AotBranchData { 12 | public let header: AotBranchDataHeader 13 | public let offset: Int 14 | } 15 | 16 | extension AotBranchData { 17 | public func compactEntries(in cache: AotCache) -> DataSequence? { 18 | guard header.kind == 1 else { return nil } 19 | return cache.fileHandle.readDataSequence( 20 | offset: UInt64(offset + AotBranchDataHeader.layoutSize), 21 | numberOfElements: numericCast(header.entry_count) 22 | ) 23 | } 24 | 25 | public func entries(in cache: AotCache) -> DataSequence? { 26 | guard header.kind == 2 else { return nil } 27 | return cache.fileHandle.readDataSequence( 28 | offset: UInt64(offset + AotBranchDataHeader.layoutSize), 29 | numberOfElements: numericCast(header.entry_count) 30 | ) 31 | } 32 | 33 | public func extendedEntries(in cache: AotCache) -> DataSequence? { 34 | guard header.kind == 3 else { return nil } 35 | return cache.fileHandle.readDataSequence( 36 | offset: UInt64(offset + AotBranchDataHeader.layoutSize), 37 | numberOfElements: numericCast(header.entry_count) 38 | ) 39 | } 40 | } 41 | 42 | extension AotBranchData { 43 | public func compactEntries(in machO: MachOFile) -> DataSequence? { 44 | guard header.kind == 1 else { return nil } 45 | return machO.fileHandle.readDataSequence( 46 | offset: UInt64(offset + AotBranchDataHeader.layoutSize), 47 | numberOfElements: numericCast(header.entry_count) 48 | ) 49 | } 50 | 51 | public func entries(in machO: MachOFile) -> DataSequence? { 52 | guard header.kind == 2 else { return nil } 53 | return machO.fileHandle.readDataSequence( 54 | offset: UInt64(offset + AotBranchDataHeader.layoutSize), 55 | numberOfElements: numericCast(header.entry_count) 56 | ) 57 | } 58 | 59 | public func extendedEntries(in machO: MachOFile) -> DataSequence? { 60 | guard header.kind == 3 else { return nil } 61 | return machO.fileHandle.readDataSequence( 62 | offset: UInt64(offset + AotBranchDataHeader.layoutSize), 63 | numberOfElements: numericCast(header.entry_count) 64 | ) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/Codesign/CodeSignSuperBlob.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CodeSignSuperBlob.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/03/03. 6 | // 7 | // 8 | 9 | import Foundation 10 | import MachOKitC 11 | 12 | public struct CodeSignSuperBlob: LayoutWrapper, Sendable { 13 | public typealias Layout = CS_SuperBlob 14 | 15 | public var layout: Layout 16 | public let offset: Int // offset from start of linkedit_data 17 | } 18 | 19 | extension CodeSignSuperBlob { 20 | public var magic: CodeSignMagic! { 21 | .init(rawValue: layout.magic) 22 | } 23 | 24 | public var count: Int { 25 | numericCast(layout.count) 26 | } 27 | } 28 | 29 | extension CodeSignSuperBlob { 30 | /// Get indices of this SuperBlob 31 | /// - Parameter signature: ``MachOFile.CodeSign`` to which this SuperBlob belongs. 32 | /// - Returns: indices of this superBlob 33 | public func blobIndices( 34 | in signature: MachOFile.CodeSign 35 | ) -> AnyRandomAccessCollection { 36 | let offset = offset + layoutSize 37 | 38 | return AnyRandomAccessCollection( 39 | DataSequence( 40 | data: try! signature.fileSlice.readData( 41 | offset: offset, 42 | upToCount: signature.fileSlice.size 43 | ), 44 | numberOfElements: count 45 | ).lazy.map { 46 | .init(layout: signature.isSwapped ? $0.swapped : $0) 47 | } 48 | ) 49 | } 50 | 51 | /// Get indices of this SuperBlob 52 | /// - Parameter signature: ``MachOImage.CodeSign`` to which this SuperBlob belongs. 53 | /// - Returns: indices of this superBlob 54 | public func blobIndices( 55 | in signature: MachOImage.CodeSign 56 | ) -> AnyRandomAccessCollection { 57 | let offset = offset + layoutSize 58 | 59 | return AnyRandomAccessCollection( 60 | MemorySequence( 61 | basePointer: signature.basePointer 62 | .advanced(by: offset) 63 | .assumingMemoryBound(to: CS_BlobIndex.self), 64 | numberOfElements: count 65 | ).lazy.map { 66 | .init(layout: signature.isSwapped ? $0.swapped : $0) 67 | } // swiftlint:disable:this closure_end_indentation 68 | ) 69 | } 70 | } 71 | 72 | extension CS_SuperBlob { 73 | var isSwapped: Bool { 74 | magic < 0xfade0000 75 | } 76 | 77 | var swapped: CS_SuperBlob { 78 | var swapped = CS_SuperBlob() 79 | swapped.magic = magic.byteSwapped 80 | swapped.length = length.byteSwapped 81 | swapped.count = count.byteSwapped 82 | return swapped 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Sources/MachOKit/LoadCommand/SegmentCommand+Flags.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SegmentCommand+Flags.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/01. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct SegmentCommandFlags: BitFlags { 12 | public typealias RawValue = UInt32 13 | 14 | public let rawValue: RawValue 15 | 16 | public init(rawValue: RawValue) { 17 | self.rawValue = rawValue 18 | } 19 | } 20 | 21 | extension SegmentCommandFlags { 22 | /// SG_HIGHVM 23 | public static let highvm = SegmentCommandFlags( 24 | rawValue: Bit.highvm.rawValue 25 | ) 26 | /// SG_FVMLIB 27 | public static let fvmlib = SegmentCommandFlags( 28 | rawValue: Bit.fvmlib.rawValue 29 | ) 30 | /// SG_NORELOC 31 | public static let noreloc = SegmentCommandFlags( 32 | rawValue: Bit.noreloc.rawValue 33 | ) 34 | /// SG_PROTECTED_VERSION_1 35 | public static let protected_version_1 = SegmentCommandFlags( 36 | rawValue: Bit.protected_version_1.rawValue 37 | ) 38 | /// SG_READ_ONLY 39 | public static let read_only = SegmentCommandFlags( 40 | rawValue: Bit.read_only.rawValue 41 | ) 42 | } 43 | 44 | extension SegmentCommandFlags { 45 | public enum Bit: CaseIterable { 46 | /// SG_HIGHVM 47 | case highvm 48 | /// SG_FVMLIB 49 | case fvmlib 50 | /// SG_NORELOC 51 | case noreloc 52 | /// SG_PROTECTED_VERSION_1 53 | case protected_version_1 54 | /// SG_READ_ONLY 55 | case read_only 56 | } 57 | } 58 | 59 | extension SegmentCommandFlags.Bit: RawRepresentable { 60 | public typealias RawValue = UInt32 61 | 62 | public init?(rawValue: UInt32) { 63 | switch rawValue { 64 | case RawValue(SG_HIGHVM): self = .highvm 65 | case RawValue(SG_FVMLIB): self = .fvmlib 66 | case RawValue(SG_NORELOC): self = .noreloc 67 | case RawValue(SG_PROTECTED_VERSION_1): self = .protected_version_1 68 | case RawValue(SG_READ_ONLY): self = .read_only 69 | default: return nil 70 | } 71 | } 72 | 73 | public var rawValue: UInt32 { 74 | switch self { 75 | case .highvm: RawValue(SG_HIGHVM) 76 | case .fvmlib: RawValue(SG_FVMLIB) 77 | case .noreloc: RawValue(SG_NORELOC) 78 | case .protected_version_1: RawValue(SG_PROTECTED_VERSION_1) 79 | case .read_only: RawValue(SG_READ_ONLY) 80 | } 81 | } 82 | } 83 | 84 | extension SegmentCommandFlags.Bit: CustomStringConvertible { 85 | public var description: String { 86 | switch self { 87 | case .highvm: "SG_HIGHVM" 88 | case .fvmlib: "SG_FVMLIB" 89 | case .noreloc: "SG_NORELOC" 90 | case .protected_version_1: "SG_PROTECTED_VERSION_1" 91 | case .read_only: "SG_READ_ONLY" 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /Sources/MachOKit/Util/Sequence/MemorySequence.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MemorySequence.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/11/29. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct MemorySequence: Sequence { 12 | public typealias Element = T 13 | 14 | private let basePointer: UnsafeRawPointer 15 | private let entrySize: Int 16 | private let numberOfElements: Int 17 | 18 | @_spi(Support) 19 | public init( 20 | basePointer: UnsafePointer, 21 | numberOfElements: Int 22 | ) { 23 | self.basePointer = .init(basePointer) 24 | self.entrySize = MemoryLayout.size 25 | self.numberOfElements = numberOfElements 26 | } 27 | 28 | @_spi(Support) 29 | public init( 30 | basePointer: UnsafePointer, 31 | entrySize: Int, 32 | numberOfElements: Int 33 | ) { 34 | self.basePointer = .init(basePointer) 35 | self.entrySize = entrySize 36 | self.numberOfElements = numberOfElements 37 | } 38 | 39 | public func makeIterator() -> Iterator { 40 | Iterator( 41 | basePointer: basePointer, 42 | entrySize: entrySize, 43 | numberOfElements: numberOfElements 44 | ) 45 | } 46 | } 47 | 48 | extension MemorySequence { 49 | public struct Iterator: IteratorProtocol { 50 | public typealias Element = T 51 | 52 | private let basePointer: UnsafeRawPointer 53 | private let entrySize: Int 54 | private let numberOfElements: Int 55 | 56 | private var nextIndex: Int = 0 57 | 58 | init( 59 | basePointer: UnsafeRawPointer, 60 | entrySize: Int, 61 | numberOfElements: Int 62 | ) { 63 | self.basePointer = basePointer 64 | self.entrySize = entrySize 65 | self.numberOfElements = numberOfElements 66 | } 67 | 68 | public mutating func next() -> Element? { 69 | guard nextIndex < numberOfElements else { return nil } 70 | defer { nextIndex += 1 } 71 | return basePointer 72 | .advanced(by: nextIndex * entrySize) 73 | .load(as: Element.self) 74 | } 75 | } 76 | } 77 | 78 | extension MemorySequence: Collection { 79 | public typealias Index = Int 80 | 81 | public var startIndex: Index { 0 } 82 | public var endIndex: Index { numberOfElements } 83 | 84 | public func index(after i: Int) -> Int { 85 | i + 1 86 | } 87 | 88 | public subscript(position: Int) -> Element { 89 | precondition(position >= 0) 90 | precondition(position < endIndex) 91 | return basePointer 92 | .advanced(by: position * entrySize) 93 | .load(as: Element.self) 94 | } 95 | } 96 | 97 | extension MemorySequence: RandomAccessCollection {} 98 | -------------------------------------------------------------------------------- /Sources/MachOKit/Header/Magic.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Magic.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/01. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public enum Magic: CaseIterable, Sendable, Codable, Equatable { 12 | /// MH_MAGIC 13 | case magic 14 | /// MH_CIGAM 15 | case cigam 16 | 17 | /// MH_MAGIC_64 18 | case magic64 19 | /// MH_CIGAM_64 20 | case cigam64 21 | 22 | /// FAT_MAGIC 23 | case fatMagic 24 | /// FAT_CIGAM 25 | case fatCigam 26 | 27 | /// FAT_MAGIC_64 28 | case fatMagic64 29 | /// FAT_CIGAM_64 30 | case fatCigam64 31 | } 32 | 33 | extension Magic { 34 | public var isFat: Bool { 35 | [.fatMagic, .fatCigam, .fatMagic64, .fatCigam64].contains(self) 36 | } 37 | 38 | public var isMach: Bool { 39 | [.magic, .magic64, .cigam, .cigam64].contains(self) 40 | } 41 | 42 | public var is64BitMach: Bool { 43 | [.magic64, .cigam64].contains(self) 44 | } 45 | 46 | public var is64BitFat: Bool { 47 | [.fatMagic64, .fatCigam64].contains(self) 48 | } 49 | 50 | public var is64Bit: Bool { 51 | is64BitMach || is64BitFat 52 | } 53 | 54 | public var isSwapped: Bool { 55 | [.cigam, .cigam64, .fatCigam, .fatCigam64].contains(self) 56 | } 57 | } 58 | 59 | extension Magic: RawRepresentable { 60 | public typealias RawValue = UInt32 61 | 62 | public init?(rawValue: UInt32) { 63 | switch rawValue { 64 | case MH_MAGIC: self = .magic 65 | case MH_CIGAM: self = .cigam 66 | case MH_MAGIC_64: self = .magic64 67 | case MH_CIGAM_64: self = .cigam64 68 | case FAT_MAGIC: self = .fatMagic 69 | case FAT_CIGAM: self = .fatCigam 70 | case FAT_MAGIC_64: self = .fatMagic64 71 | case FAT_CIGAM_64: self = .fatCigam64 72 | default: return nil 73 | } 74 | } 75 | 76 | public var rawValue: UInt32 { 77 | switch self { 78 | case .magic: MH_MAGIC 79 | case .cigam: MH_CIGAM 80 | case .magic64: MH_MAGIC_64 81 | case .cigam64: MH_CIGAM_64 82 | case .fatMagic: FAT_MAGIC 83 | case .fatCigam: FAT_CIGAM 84 | case .fatMagic64: FAT_MAGIC_64 85 | case .fatCigam64: FAT_CIGAM_64 86 | } 87 | } 88 | } 89 | 90 | extension Magic: CustomStringConvertible { 91 | public var description: String { 92 | switch self { 93 | case .magic: "MH_MAGIC" 94 | case .cigam: "MH_CIGAM" 95 | case .magic64: "MH_MAGIC_64" 96 | case .cigam64: "MH_CIGAM_64" 97 | case .fatMagic: "FAT_MAGIC" 98 | case .fatCigam: "FAT_CIGAM" 99 | case .fatMagic64: "FAT_MAGIC_64" 100 | case .fatCigam64: "FAT_CIGAM_64" 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Sources/MachOKit/AotCache+CodeFragments.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AotCache+CodeFragments.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2025/12/15 6 | // 7 | // 8 | 9 | import Foundation 10 | import MachOKitC 11 | #if compiler(>=6.0) || (compiler(>=5.10) && hasFeature(AccessLevelOnImport)) 12 | internal import FileIO 13 | #else 14 | @_implementationOnly import FileIO 15 | #endif 16 | 17 | extension AotCache { 18 | public struct CodeFragments: Sequence { 19 | typealias FileSlice = File.FileSlice 20 | 21 | private let fileSlice: FileSlice 22 | 23 | public let offset: Int 24 | public let size: Int 25 | 26 | public let numberOfEntries: Int 27 | 28 | init( 29 | fileSlice: FileSlice, 30 | offset: Int, 31 | size: Int, 32 | numberOfEntries: Int 33 | ) { 34 | self.fileSlice = fileSlice 35 | self.offset = offset 36 | self.size = size 37 | self.numberOfEntries = numberOfEntries 38 | } 39 | 40 | public func makeIterator() -> Iterator { 41 | .init( 42 | fileSlice: fileSlice, 43 | numberOfEntries: numberOfEntries 44 | ) 45 | } 46 | } 47 | } 48 | 49 | extension AotCache.CodeFragments { 50 | public var data: Data? { 51 | try? fileSlice.readAllData() 52 | } 53 | } 54 | 55 | extension AotCache.CodeFragments { 56 | public struct Iterator: IteratorProtocol { 57 | public typealias Element = AotCacheCodeFragment 58 | 59 | private let fileSlice: FileSlice 60 | private let numberOfEntries: Int 61 | 62 | private var nextOffset: Int = 0 63 | private var nextIndex: Int = 0 64 | 65 | init(fileSlice: FileSlice, numberOfEntries: Int) { 66 | self.fileSlice = fileSlice 67 | self.numberOfEntries = numberOfEntries 68 | } 69 | 70 | public mutating func next() -> Element? { 71 | guard nextOffset < fileSlice.size else { return nil } 72 | guard nextIndex < numberOfEntries else { return nil } 73 | 74 | let layout: Element.Layout? = try? fileSlice.read( 75 | offset: nextOffset 76 | ) 77 | guard let layout else { return nil } 78 | 79 | guard AotCodeFragmentType(rawValue: layout.type) != nil else { 80 | return nil 81 | } 82 | 83 | defer { 84 | nextOffset += Element.layoutSize 85 | nextOffset += numericCast(layout.branch_data_size) 86 | nextOffset += numericCast(layout.instruction_map_size) 87 | 88 | nextIndex += 1 89 | } 90 | 91 | let offset = fileSlice.baseOffset + nextOffset 92 | return .init(layout: layout, offset: offset) 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Sources/MachOKitC/include/backports.h: -------------------------------------------------------------------------------- 1 | // 2 | // backports.h 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2024/11/29 6 | // 7 | // 8 | 9 | #ifndef backports_h 10 | #define backports_h 11 | 12 | #ifndef __linux__ 13 | 14 | #include 15 | #include 16 | 17 | #ifndef MH_IMPLICIT_PAGEZERO 18 | #define MH_IMPLICIT_PAGEZERO 0x10000000 19 | #endif 20 | 21 | #ifndef DYLIB_USE_MARKER 22 | struct dylib_use_command { 23 | uint32_t cmd; /* LC_LOAD_DYLIB or LC_LOAD_WEAK_DYLIB */ 24 | uint32_t cmdsize; /* overall size, including path */ 25 | uint32_t nameoff; /* == 28, dylibs's path offset */ 26 | uint32_t marker; /* == DYLIB_USE_MARKER */ 27 | uint32_t current_version; /* dylib's current version number */ 28 | uint32_t compat_version; /* dylib's compatibility version number */ 29 | uint32_t flags; /* DYLIB_USE_... flags */ 30 | }; 31 | #define DYLIB_USE_WEAK_LINK 0x01 32 | #define DYLIB_USE_REEXPORT 0x02 33 | #define DYLIB_USE_UPWARD 0x04 34 | #define DYLIB_USE_DELAYED_INIT 0x08 35 | 36 | #define DYLIB_USE_MARKER 0x1a741800 37 | #endif 38 | 39 | #ifndef PLATFORM_VISIONOS 40 | #define PLATFORM_VISIONOS 11 41 | #endif 42 | 43 | #ifndef PLATFORM_VISIONOSSIMULATOR 44 | #define PLATFORM_VISIONOSSIMULATOR 12 45 | #endif 46 | 47 | #ifndef LC_FUNCTION_VARIANTS 48 | #define LC_FUNCTION_VARIANTS 0x37 /* used with linkedit_data_command */ 49 | #endif 50 | 51 | #ifndef LC_FUNCTION_VARIANT_FIXUPS 52 | #define LC_FUNCTION_VARIANT_FIXUPS 0x38 /* used with linkedit_data_command */ 53 | #endif 54 | 55 | #ifndef LC_TARGET_TRIPLE 56 | #define LC_TARGET_TRIPLE 0x39 /* target triple used to compile */ 57 | 58 | /* 59 | * The target_triple_command contains a string which specifies the 60 | * target triple (e.g. "arm64e-apple-macosx15.0.0") used to compile the code. 61 | */ 62 | struct target_triple_command { 63 | uint32_t cmd; /* LC_TARGET_TRIPLE */ 64 | uint32_t cmdsize; /* including string */ 65 | union lc_str triple; /* target triple string */ 66 | }; 67 | #endif 68 | 69 | #ifndef PLATFORM_MACOS_EXCLAVECORE 70 | #define PLATFORM_MACOS_EXCLAVECORE 15 71 | #define PLATFORM_MACOS_EXCLAVEKIT 16 72 | #define PLATFORM_IOS_EXCLAVECORE 17 73 | #define PLATFORM_IOS_EXCLAVEKIT 18 74 | #define PLATFORM_TVOS_EXCLAVECORE 19 75 | #define PLATFORM_TVOS_EXCLAVEKIT 20 76 | #define PLATFORM_WATCHOS_EXCLAVECORE 21 77 | #define PLATFORM_WATCHOS_EXCLAVEKIT 22 78 | #define PLATFORM_VISIONOS_EXCLAVECORE 23 79 | #define PLATFORM_VISIONOS_EXCLAVEKIT 24 80 | #endif 81 | 82 | #endif /* __linux__ */ 83 | 84 | #ifndef EXPORT_SYMBOL_FLAGS_FUNCTION_VARIANT 85 | #define EXPORT_SYMBOL_FLAGS_FUNCTION_VARIANT 0x20 86 | #endif 87 | 88 | #endif /* backports_h */ 89 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*.*.*' 7 | workflow_dispatch: 8 | 9 | env: 10 | DEVELOPER_DIR: /Applications/Xcode_15.3.app 11 | 12 | jobs: 13 | build: 14 | name: Release 15 | runs-on: macos-14 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v4 19 | 20 | - name: Select Xcode 15 21 | run: sudo xcode-select -s /Applications/Xcode_15.3.app 22 | 23 | - name: Install visionOS 24 | run: | 25 | sudo xcodebuild -runFirstLaunch 26 | sudo xcrun simctl list 27 | sudo xcodebuild -downloadPlatform visionOS 28 | sudo xcodebuild -runFirstLaunch 29 | 30 | - name: Build 31 | run: bash scripts/xcframework.sh 32 | 33 | - name: Check Sum 34 | run: | 35 | machokit="$(swift package compute-checksum XCFrameworks/MachOKit.xcframework.zip)" 36 | machokitc="$(swift package compute-checksum XCFrameworks/MachOKitC.xcframework.zip)" 37 | echo "machokit_checksum=$machokit" >> $GITHUB_ENV 38 | echo "machokitc_checksum=$machokitc" >> $GITHUB_ENV 39 | echo "MachOKit $machokit" 40 | echo "MachOKitC $machokitc" 41 | 42 | - name: Get tag version 43 | id: get_tag_version 44 | run: | 45 | echo "$(git tag --sort=creatordate | tail -n 1)" > CURRENT_TAG 46 | echo "current_tag=$(cat CURRENT_TAG)" >> $GITHUB_OUTPUT 47 | echo "previous_tag=$(\ 48 | curl -H 'Accept: application/vnd.github.v3+json' \ 49 | -H 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' \ 50 | https://api.github.com/repos/${{ github.repository }}/releases/latest \ 51 | | jq -r .tag_name)\ 52 | " >> $GITHUB_OUTPUT 53 | 54 | - name: Generate release note 55 | id: release_note 56 | run: | 57 | echo -e "$(\ 58 | curl -X POST \ 59 | -H 'Accept: application/vnd.github.v3+json' \ 60 | -H 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' \ 61 | https://api.github.com/repos/${{ github.repository }}/releases/generate-notes \ 62 | -d '{"tag_name":"${{ steps.get_tag_version.outputs.current_tag }}", "previous_tag_name":"${{ steps.get_tag_version.outputs.previous_tag }}"}' \ 63 | | jq .body| sed 's/"//g'\ 64 | )" > release_body.txt 65 | echo -e "\n" >> release_body.txt 66 | echo -e "MachOKit\n$machokit_checksum\n" >> release_body.txt 67 | echo -e "MachOKitC\n$machokitc_checksum" >> release_body.txt 68 | 69 | - name: Release 70 | env: 71 | GITHUB_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }} 72 | uses: softprops/action-gh-release@v1 73 | with: 74 | body_path: "./release_body.txt" 75 | files: | 76 | ./XCFrameworks/MachOKit.xcframework.zip 77 | ./XCFrameworks/MachOKitC.xcframework.zip 78 | 79 | docc: 80 | name: Deploy Document 81 | needs: [build] 82 | uses: ./.github/workflows/docc.yml 83 | -------------------------------------------------------------------------------- /Sources/MachOKit/Protocol/LoadCommandsProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoadCommandsProtocol.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/04. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol LoadCommandsProtocol: Sequence {} 12 | 13 | extension LoadCommandsProtocol { 14 | public func of(_ type: LoadCommandType) -> AnySequence { 15 | AnySequence( 16 | lazy.filter { 17 | $0.type == type 18 | } 19 | ) 20 | } 21 | 22 | public func infos( 23 | of type: @escaping (T) -> LoadCommand 24 | ) -> AnySequence { 25 | AnySequence( 26 | lazy.compactMap { cmd in 27 | guard let info = cmd.info as? T else { return nil } 28 | guard type(info).type == cmd.type else { return nil } 29 | return info 30 | } 31 | ) 32 | } 33 | 34 | public func info( 35 | of type: @escaping (T) -> LoadCommand 36 | ) -> T? { 37 | infos(of: type) 38 | .first(where: { _ in true }) 39 | } 40 | } 41 | 42 | extension LoadCommandsProtocol { 43 | var text: SegmentCommand? { 44 | infos(of: LoadCommand.segment) 45 | .first { 46 | $0.segname == SEG_TEXT 47 | } 48 | } 49 | 50 | var text64: SegmentCommand64? { 51 | infos(of: LoadCommand.segment64) 52 | .first { 53 | $0.segname == SEG_TEXT 54 | } 55 | } 56 | 57 | var linkedit: SegmentCommand? { 58 | infos(of: LoadCommand.segment) 59 | .first { 60 | $0.segname == SEG_LINKEDIT 61 | } 62 | } 63 | 64 | var linkedit64: SegmentCommand64? { 65 | infos(of: LoadCommand.segment64) 66 | .first { 67 | $0.segname == SEG_LINKEDIT 68 | } 69 | } 70 | 71 | var symtab: LoadCommandInfo? { 72 | info(of: LoadCommand.symtab) 73 | } 74 | 75 | var dysymtab: LoadCommandInfo? { 76 | info(of: LoadCommand.dysymtab) 77 | } 78 | 79 | var functionStarts: LoadCommandInfo? { 80 | info(of: LoadCommand.functionStarts) 81 | } 82 | 83 | var dataInCode: LoadCommandInfo? { 84 | info(of: LoadCommand.dataInCode) 85 | } 86 | 87 | var dyldChainedFixups: LoadCommandInfo? { 88 | info(of: LoadCommand.dyldChainedFixups) 89 | } 90 | 91 | var idDylib: DylibCommand? { 92 | info(of: LoadCommand.idDylib) 93 | } 94 | 95 | var encryptionInfo: EncryptionInfoCommand? { 96 | info(of: LoadCommand.encryptionInfo) 97 | } 98 | 99 | var encryptionInfo64: EncryptionInfoCommand64? { 100 | info(of: LoadCommand.encryptionInfo64) 101 | } 102 | 103 | var codeSignature: LoadCommandInfo? { 104 | info(of: LoadCommand.codeSignature) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /Sources/MachOKit/Header/MachHeader/FileType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FileType.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/11/29. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public enum FileType: Sendable { 12 | /// MH_OBJECT 13 | case object 14 | /// MH_EXECUTE 15 | case execute 16 | /// MH_FVMLIB 17 | case fvmlib 18 | /// MH_CORE 19 | case core 20 | /// MH_PRELOAD 21 | case preload 22 | /// MH_DYLIB 23 | case dylib 24 | /// MH_DYLINKER 25 | case dylinker 26 | /// MH_BUNDLE 27 | case bundle 28 | /// MH_DYLIB_STUB 29 | case dylibStub 30 | /// MH_DSYM 31 | case dsym 32 | /// MH_KEXT_BUNDLE 33 | case kextBundle 34 | /// MH_FILESET 35 | case fileset 36 | /// MH_GPU_EXECUTE 37 | case gpuExecute 38 | /// MH_GPU_DYLIB 39 | case gpuDylib 40 | } 41 | 42 | extension FileType: RawRepresentable { 43 | public typealias RawValue = Int32 44 | 45 | public init?(rawValue: Int32) { 46 | switch rawValue { 47 | case MH_OBJECT: self = .object 48 | case MH_EXECUTE: self = .execute 49 | case MH_FVMLIB: self = .fvmlib 50 | case MH_CORE: self = .core 51 | case MH_PRELOAD: self = .preload 52 | case MH_DYLIB: self = .dylib 53 | case MH_DYLINKER: self = .dylinker 54 | case MH_BUNDLE: self = .bundle 55 | case MH_DYLIB_STUB: self = .dylibStub 56 | case MH_DSYM: self = .dsym 57 | case MH_KEXT_BUNDLE: self = .kextBundle 58 | case MH_FILESET: self = .fileset 59 | case MH_GPU_EXECUTE: self = .gpuExecute 60 | case MH_GPU_DYLIB: self = .gpuDylib 61 | default: return nil 62 | } 63 | } 64 | 65 | public var rawValue: Int32 { 66 | switch self { 67 | case .object: MH_OBJECT 68 | case .execute: MH_EXECUTE 69 | case .fvmlib: MH_FVMLIB 70 | case .core: MH_CORE 71 | case .preload: MH_PRELOAD 72 | case .dylib: MH_DYLIB 73 | case .dylinker: MH_DYLINKER 74 | case .bundle: MH_BUNDLE 75 | case .dylibStub: MH_DYLIB_STUB 76 | case .dsym: MH_DSYM 77 | case .kextBundle: MH_KEXT_BUNDLE 78 | case .fileset: MH_FILESET 79 | case .gpuExecute: MH_GPU_EXECUTE 80 | case .gpuDylib: MH_GPU_DYLIB 81 | } 82 | } 83 | } 84 | 85 | extension FileType: CustomStringConvertible { 86 | public var description: String { 87 | switch self { 88 | case .object: "MH_OBJECT" 89 | case .execute: "MH_EXECUTE" 90 | case .fvmlib: "MH_FVMLIB" 91 | case .core: "MH_CORE" 92 | case .preload: "MH_PRELOAD" 93 | case .dylib: "MH_DYLIB" 94 | case .dylinker: "MH_DYLINKER" 95 | case .bundle: "MH_BUNDLE" 96 | case .dylibStub: "MH_DYLIB_STUB" 97 | case .dsym: "MH_DSYM" 98 | case .kextBundle: "MH_KEXT_BUNDLE" 99 | case .fileset: "MH_FILESET" 100 | case .gpuExecute: "MH_GPU_EXECUTE" 101 | case .gpuDylib: "MH_GPU_DYLIB" 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Sources/MachOKit/LoadCommand/DylibCommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DylibCommand.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/11/29. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct DylibCommand: LoadCommandWrapper { 12 | public typealias Layout = dylib_command 13 | 14 | public var layout: Layout 15 | public var offset: Int // offset from mach header trailing 16 | 17 | init(_ layout: Layout, offset: Int) { 18 | self.layout = layout 19 | self.offset = offset 20 | } 21 | } 22 | 23 | extension DylibCommand { 24 | public func dylib(cmdsStart: UnsafeRawPointer) -> Dylib { 25 | let ptr = cmdsStart 26 | .advanced(by: offset) 27 | .advanced(by: Int(layout.dylib.name.offset)) 28 | .assumingMemoryBound(to: CChar.self) 29 | 30 | return .init( 31 | name: String(cString: ptr), 32 | timestamp: Date(timeIntervalSince1970: TimeInterval(layout.dylib.timestamp)), 33 | currentVersion: .init(layout.dylib.current_version), 34 | compatibilityVersion: .init(layout.dylib.compatibility_version) 35 | ) 36 | } 37 | } 38 | 39 | extension DylibCommand { 40 | public func dylib(in machO: MachOFile) -> Dylib { 41 | let offset = machO.cmdsStartOffset + offset + Int(layout.dylib.name.offset) 42 | // swap is not needed 43 | let string = machO.fileHandle.readString( 44 | offset: numericCast(offset), 45 | size: Int(layout.cmdsize) - layoutSize 46 | ) ?? "" 47 | 48 | return .init( 49 | name: string, 50 | timestamp: Date(timeIntervalSince1970: TimeInterval(layout.dylib.timestamp)), 51 | currentVersion: .init(layout.dylib.current_version), 52 | compatibilityVersion: .init(layout.dylib.compatibility_version) 53 | ) 54 | } 55 | } 56 | 57 | extension DylibCommand { 58 | public var isDylibUseCommand: Bool { 59 | layout.dylib.timestamp == DYLIB_USE_MARKER 60 | } 61 | 62 | public func dylibUseCommand(in machO: MachOImage) -> DylibUseCommand? { 63 | guard isDylibUseCommand else { return nil } 64 | 65 | let ptr = machO.cmdsStartPtr 66 | .advanced(by: offset) 67 | .assumingMemoryBound(to: DylibUseCommand.Layout.self) 68 | return .init(ptr.pointee, offset: offset) 69 | } 70 | 71 | public func dylibUseCommand(in machO: MachOFile) -> DylibUseCommand? { 72 | guard isDylibUseCommand else { return nil } 73 | 74 | let offset = machO.cmdsStartOffset + offset 75 | let layout: DylibUseCommand.Layout = machO.fileHandle.read( 76 | offset: numericCast(offset), 77 | swapHandler: { 78 | guard machO.isSwapped else { return } 79 | return $0.withUnsafeBytes { 80 | guard let baseAddress = $0.baseAddress else { return } 81 | let ptr = UnsafeMutableRawPointer(mutating: baseAddress) 82 | .assumingMemoryBound(to: dylib_use_command.self) 83 | swap_dylib_use_command(ptr, NXHostByteOrder()) 84 | } 85 | } 86 | ) 87 | return .init(layout, offset: offset) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/Codesign/CodeDirectory/CodeSignCodeDirectory+runtime.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CodeSignCodeDirectory+runtime.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/03/11. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | extension CodeSignCodeDirectory { 12 | public func runtime(in signature: MachOFile.CodeSign) -> Runtime? { 13 | guard isSupportsRuntime else { 14 | return nil 15 | } 16 | let layout: CS_CodeDirectory_Runtime = signature.fileSlice.ptr 17 | .advanced(by: offset) 18 | .advanced(by: layoutSize) 19 | .advanced(by: ScatterOffset.layoutSize) 20 | .advanced(by: TeamIdOffset.layoutSize) 21 | .advanced(by: CodeLimit64.layoutSize) 22 | .advanced(by: ExecutableSegment.layoutSize) 23 | .assumingMemoryBound(to: CS_CodeDirectory_Runtime.self) 24 | .pointee 25 | return .init( 26 | layout: signature.isSwapped ? layout.swapped : layout 27 | ) 28 | } 29 | 30 | public func runtime(in signature: MachOImage.CodeSign) -> Runtime? { 31 | guard isSupportsRuntime else { 32 | return nil 33 | } 34 | let layout: CS_CodeDirectory_Runtime? = signature.basePointer 35 | .advanced(by: offset) 36 | .advanced(by: layoutSize) 37 | .advanced(by: ScatterOffset.layoutSize) 38 | .advanced(by: TeamIdOffset.layoutSize) 39 | .advanced(by: CodeLimit64.layoutSize) 40 | .advanced(by: ExecutableSegment.layoutSize) 41 | .assumingMemoryBound(to: CS_CodeDirectory_Runtime.self) 42 | .pointee 43 | guard let layout else { return nil } 44 | return .init( 45 | layout: signature.isSwapped ? layout.swapped : layout 46 | ) 47 | } 48 | } 49 | 50 | extension CodeSignCodeDirectory { 51 | public func preEncryptHash( 52 | forSlot index: Int, 53 | in signature: MachOFile.CodeSign 54 | ) -> Data? { 55 | guard index >= 0, 56 | index < Int(layout.nCodeSlots), 57 | let runtime = runtime(in: signature), 58 | runtime.preEncryptOffset != 0 else { 59 | return nil 60 | } 61 | let size: Int = numericCast(layout.hashSize) 62 | let offset = offset 63 | + numericCast(runtime.preEncryptOffset) 64 | + index * size 65 | return try! signature.fileSlice.readData( 66 | offset: numericCast(offset), 67 | length: size 68 | ) 69 | } 70 | 71 | public func preEncryptHash( 72 | forSlot index: Int, 73 | in signature: MachOImage.CodeSign 74 | ) -> Data? { 75 | guard index >= 0, 76 | index < Int(layout.nCodeSlots), 77 | let runtime = runtime(in: signature), 78 | runtime.preEncryptOffset != 0 else { 79 | return nil 80 | } 81 | let size: Int = numericCast(layout.hashSize) 82 | let offset = offset 83 | + numericCast(runtime.preEncryptOffset) 84 | + index * size 85 | return Data( 86 | bytes: signature.basePointer.advanced(by: offset), 87 | count: size 88 | ) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldCache/SlideInfo/DyldCacheSlideInfo4.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldCacheSlideInfo4.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/07/24 6 | // 7 | // 8 | 9 | import Foundation 10 | import MachOKitC 11 | 12 | public struct DyldCacheSlideInfo4: LayoutWrapper, Sendable { 13 | public typealias Layout = dyld_cache_slide_info4 14 | 15 | public var layout: Layout 16 | public var offset: Int 17 | } 18 | 19 | // MARK: - PageStart 20 | extension DyldCacheSlideInfo4 { 21 | public struct PageStart { 22 | public let value: UInt16 23 | 24 | public var isNoRebase: Bool { 25 | value == DYLD_CACHE_SLIDE4_PAGE_NO_REBASE 26 | } 27 | 28 | public var isUseExtra: Bool { 29 | (value & UInt16(DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA)) > 0 30 | } 31 | 32 | public var extrasStartIndex: Int? { 33 | guard isUseExtra else { return nil } 34 | return numericCast(value & UInt16(DYLD_CACHE_SLIDE4_PAGE_INDEX)) 35 | } 36 | } 37 | } 38 | 39 | // MARK: - PageExtra 40 | extension DyldCacheSlideInfo4 { 41 | public struct PageExtra { 42 | public let value: UInt16 43 | 44 | public var isEnd: Bool { 45 | (value & UInt16(DYLD_CACHE_SLIDE4_PAGE_EXTRA_END)) > 0 46 | } 47 | } 48 | } 49 | 50 | // MARK: - function & proerty 51 | extension DyldCacheSlideInfo4 { 52 | public var pageSize: Int { 53 | numericCast(layout.page_size) 54 | } 55 | } 56 | 57 | extension DyldCacheSlideInfo4 { 58 | public var numberOfPageStarts: Int { 59 | numericCast(layout.page_starts_count) 60 | } 61 | 62 | public func pageStarts(in cache: DyldCache) -> DataSequence? { 63 | _pageStarts(in: cache) 64 | } 65 | 66 | public func pageStarts(in cache: FullDyldCache) -> DataSequence? { 67 | _pageStarts(in: cache) 68 | } 69 | } 70 | 71 | extension DyldCacheSlideInfo4 { 72 | internal func _pageStarts( 73 | in cache: Cache 74 | ) -> DataSequence? { 75 | cache.fileHandle.readDataSequence( 76 | offset: numericCast(offset) + numericCast(layout.page_starts_offset), 77 | numberOfElements: numberOfPageStarts 78 | ) 79 | } 80 | } 81 | 82 | extension DyldCacheSlideInfo4 { 83 | public var numberOfPageExtras: Int { 84 | numericCast(layout.page_extras_count) 85 | } 86 | 87 | public func pageExtras(in cache: DyldCache) -> DataSequence? { 88 | _pageExtras(in: cache) 89 | } 90 | 91 | public func pageExtras(in cache: FullDyldCache) -> DataSequence? { 92 | _pageExtras(in: cache) 93 | } 94 | } 95 | 96 | extension DyldCacheSlideInfo4 { 97 | internal func _pageExtras( 98 | in cache: Cache 99 | ) -> DataSequence? { 100 | guard layout.page_extras_offset > 0 else { return nil } 101 | return cache.fileHandle.readDataSequence( 102 | offset: numericCast(offset) + numericCast(layout.page_extras_offset), 103 | numberOfElements: numberOfPageExtras 104 | ) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /Sources/MachOKit/Util/Sequence/DataSequence.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DataSequence.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/06. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct DataSequence: Sequence { 12 | public typealias Element = T 13 | 14 | private let data: Data 15 | private let entrySize: Int 16 | private let numberOfElements: Int 17 | 18 | @_spi(Support) 19 | public init( 20 | data: Data, 21 | numberOfElements: Int 22 | ) { 23 | self.data = data 24 | self.entrySize = MemoryLayout.size 25 | self.numberOfElements = numberOfElements 26 | } 27 | 28 | @_spi(Support) 29 | public init( 30 | data: Data, 31 | entrySize: Int 32 | ) { 33 | self.data = data 34 | self.entrySize = entrySize 35 | self.numberOfElements = data.count / entrySize 36 | } 37 | 38 | public func makeIterator() -> Iterator { 39 | Iterator( 40 | data: data, 41 | entrySize: entrySize, 42 | numberOfElements: numberOfElements 43 | ) 44 | } 45 | } 46 | 47 | extension DataSequence { 48 | public struct Iterator: IteratorProtocol { 49 | public typealias Element = T 50 | 51 | private let data: Data 52 | private let entrySize: Int 53 | private let numberOfElements: Int 54 | 55 | private var nextIndex: Int = 0 56 | private var nextOffset: Int = 0 57 | 58 | init( 59 | data: Data, 60 | entrySize: Int, 61 | numberOfElements: Int 62 | ) { 63 | self.data = data 64 | self.entrySize = entrySize 65 | self.numberOfElements = numberOfElements 66 | } 67 | 68 | public mutating func next() -> Element? { 69 | guard nextIndex < numberOfElements else { return nil } 70 | guard nextOffset + entrySize <= data.count else { return nil } 71 | 72 | defer { 73 | nextIndex += 1 74 | nextOffset += entrySize 75 | } 76 | 77 | return data.withUnsafeBytes { 78 | guard let baseAddress = $0.baseAddress else { return nil } 79 | return baseAddress.advanced(by: nextOffset).load(as: Element.self) 80 | } 81 | } 82 | } 83 | } 84 | 85 | extension DataSequence: Collection { 86 | public typealias Index = Int 87 | 88 | public var startIndex: Index { 0 } 89 | public var endIndex: Index { numberOfElements } 90 | 91 | public func index(after i: Int) -> Int { 92 | i + 1 93 | } 94 | 95 | public subscript(position: Int) -> Element { 96 | precondition(position >= 0) 97 | precondition(position < endIndex) 98 | precondition(data.count >= (position + 1) * entrySize) 99 | return data.withUnsafeBytes { 100 | guard let baseAddress = $0.baseAddress else { 101 | fatalError("data is empty") 102 | } 103 | return baseAddress 104 | .advanced(by: position * entrySize) 105 | .load(as: Element.self) 106 | } 107 | } 108 | } 109 | 110 | extension DataSequence: RandomAccessCollection {} 111 | --------------------------------------------------------------------------------