├── .github └── workflows │ ├── ci.yml │ ├── docc.yml │ ├── release.yml │ └── update-prebuild-spm.yml ├── .gitignore ├── .spi.yml ├── .swiftlint.yml ├── .swiftpm └── xcode │ └── package.xcworkspace │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── LICENSE ├── MachOKit.docc ├── MachOKit.md └── MachOKitC.md ├── Makefile ├── Package.resolved ├── Package.swift ├── README.md ├── Sources ├── MachOKit │ ├── DyldCache+SubCaches.swift │ ├── DyldCache.swift │ ├── DyldCacheLoaded+SubCaches.swift │ ├── DyldCacheLoaded+static.swift │ ├── DyldCacheLoaded.swift │ ├── Extension │ │ ├── Data+.swift │ │ ├── FileHandle+.swift │ │ ├── FixedWidthInteger+.swift │ │ ├── Sequence+.swift │ │ ├── String+.swift │ │ ├── TrieTreeProtocol+.swift │ │ ├── UUID+.swift │ │ ├── UnsafePointer+.swift │ │ ├── UnsafeRawPointer+.swift │ │ ├── _FileIOProtocol+.swift │ │ └── dyld_chained_ptr+.swift │ ├── FatFile.swift │ ├── Header │ │ ├── CPU.swift │ │ ├── CPUSubType.swift │ │ ├── CPUType.swift │ │ ├── DyldCacheHeader │ │ │ ├── DyldCacheHeader.swift │ │ │ └── DyldCacheType.swift │ │ ├── FatHeader │ │ │ ├── FatArch.swift │ │ │ └── FatHeader.swift │ │ ├── MachHeader │ │ │ ├── FileType.swift │ │ │ ├── MachHeader+Flags+Bit.swift │ │ │ ├── MachHeader+Flags.swift │ │ │ └── MachHeader.swift │ │ └── Magic.swift │ ├── LoadCommand │ │ ├── BuildVersionCommand.swift │ │ ├── DylibCommand.swift │ │ ├── DylibUseCommand.swift │ │ ├── DylinkerCommand.swift │ │ ├── EncryptionInfoCommand.swift │ │ ├── EntryPointCommand.swift │ │ ├── LinkerOptionCommand.swift │ │ ├── LoadCommand.swift │ │ ├── LoadCommandInfo.swift │ │ ├── LoadCommandType.swift │ │ ├── Model │ │ │ ├── BuildToolVersion.swift │ │ │ ├── Dylib.swift │ │ │ ├── DylibUseFlags.swift │ │ │ ├── Platform.swift │ │ │ ├── Section+Attribute.swift │ │ │ ├── Section+Flags.swift │ │ │ ├── Section+Type.swift │ │ │ ├── Section.swift │ │ │ ├── ThreadState.swift │ │ │ ├── ThreadStateFlavor.swift │ │ │ ├── Tool.swift │ │ │ ├── VMProtection.swift │ │ │ └── Version.swift │ │ ├── NoteCommand.swift │ │ ├── RpathCommand.swift │ │ ├── SegmentCommand+Flags.swift │ │ ├── SegmentCommand.swift │ │ ├── SourceVersionCommand.swift │ │ ├── TargetTripleCommand.swift │ │ ├── ThreadCommand.swift │ │ ├── UUIDCommand.swift │ │ └── VersionMinCommand.swift │ ├── MachOFile+BindOperations.swift │ ├── MachOFile+CodeSign.swift │ ├── MachOFile+DyldChainedFixups.swift │ ├── MachOFile+ExportTrie.swift │ ├── MachOFile+FunctionStarts.swift │ ├── MachOFile+LoadCommands.swift │ ├── MachOFile+RebaseOperations.swift │ ├── MachOFile+Strings.swift │ ├── MachOFile+Symbols.swift │ ├── MachOFile.swift │ ├── MachOImage+BindOperations.swift │ ├── MachOImage+CodeSign.swift │ ├── MachOImage+DyldChainedFixups..swift │ ├── MachOImage+ExportTrie.swift │ ├── MachOImage+FunctionStarts.swift │ ├── MachOImage+LoadCommands.swift │ ├── MachOImage+RebaseOperations.swift │ ├── MachOImage+Strings.swift │ ├── MachOImage+Symbols.swift │ ├── MachOImage+static.swift │ ├── MachOImage.swift │ ├── MachOKit.swift │ ├── Model │ │ ├── Bind │ │ │ ├── BindOpcode.swift │ │ │ ├── BindOperation.swift │ │ │ ├── BindOperationsKind.swift │ │ │ ├── BindSpecial.swift │ │ │ ├── BindType.swift │ │ │ └── ClassicBindType.swift │ │ ├── BindingSymbol.swift │ │ ├── ClassicBindingSymbol.swift │ │ ├── Codesign │ │ │ ├── CodeDirectory │ │ │ │ ├── CodeSignCodeDirectory+codeLimit64.swift │ │ │ │ ├── CodeSignCodeDirectory+executableSegment.swift │ │ │ │ ├── CodeSignCodeDirectory+runtime.swift │ │ │ │ ├── CodeSignCodeDirectory+scatter.swift │ │ │ │ ├── CodeSignCodeDirectory+teamID.swift │ │ │ │ └── CodeSignCodeDirectory.swift │ │ │ ├── CodeSignBlobIndex.swift │ │ │ ├── CodeSignExecSegmentFlags.swift │ │ │ ├── CodeSignGenericBlob.swift │ │ │ ├── CodeSignHashType.swift │ │ │ ├── CodeSignMagic.swift │ │ │ ├── CodeSignSlot.swift │ │ │ ├── CodeSignSpecialSlotType.swift │ │ │ └── CodeSignSuperBlob.swift │ │ ├── CoreFoundation │ │ │ └── CFString.swift │ │ ├── DataInCodeEntry.swift │ │ ├── DependedDylib.swift │ │ ├── DyldCache │ │ │ ├── DyldCacheDynamicData.swift │ │ │ ├── DyldCacheFunctionVariantEntry.swift │ │ │ ├── DyldCacheFunctionVariantInfo.swift │ │ │ ├── DyldCacheImageInfo.swift │ │ │ ├── DyldCacheImageTextInfo.swift │ │ │ ├── DyldCacheLocalSymbolsEntry.swift │ │ │ ├── DyldCacheLocalSymbolsInfo.swift │ │ │ ├── DyldCacheMappingAndSlideInfo.swift │ │ │ ├── DyldCacheMappingFlags.swift │ │ │ ├── DyldCacheMappingInfo.swift │ │ │ ├── DyldCachePrewarming.swift │ │ │ ├── DyldCachePrewarmingEntry.swift │ │ │ ├── DyldCacheTproMappingInfo.swift │ │ │ ├── DyldSubCacheEntry.swift │ │ │ ├── DylibIndex.swift │ │ │ ├── DylibsTrieNodeContent.swift │ │ │ ├── Loader │ │ │ │ ├── LoaderRef.swift │ │ │ │ ├── ObjCBinaryInfo.swift │ │ │ │ ├── PrebuiltLoader.swift │ │ │ │ ├── PrebuiltLoaderProtocol.swift │ │ │ │ ├── PrebuiltLoaderSet.swift │ │ │ │ ├── PrebuiltLoader_Pre1165_3.swift │ │ │ │ └── SectionLocations.swift │ │ │ ├── ObjCOptimization │ │ │ │ ├── ObjCHeaderInfoRO.swift │ │ │ │ ├── ObjCHeaderInfoRW.swift │ │ │ │ ├── ObjCHeaderOptimizationRO.swift │ │ │ │ ├── ObjCHeaderOptimizationRW.swift │ │ │ │ ├── ObjCImageInfo.swift │ │ │ │ ├── ObjCOptimization.swift │ │ │ │ └── OldObjCOptimization.swift │ │ │ ├── ProgramOffset.swift │ │ │ ├── ProgramsTrieNodeContent.swift │ │ │ ├── SlideInfo │ │ │ │ ├── DyldCacheSlideInfo.swift │ │ │ │ ├── DyldCacheSlideInfo1.swift │ │ │ │ ├── DyldCacheSlideInfo2.swift │ │ │ │ ├── DyldCacheSlideInfo3.swift │ │ │ │ ├── DyldCacheSlideInfo4.swift │ │ │ │ └── DyldCacheSlideInfo5.swift │ │ │ └── SwiftOptimization.swift │ │ ├── DyldChain │ │ │ ├── DyldChainedFixupPointer.swift │ │ │ ├── DyldChainedFixupPointerContent.swift │ │ │ ├── DyldChainedFixupPointerFormat.swift │ │ │ ├── DyldChainedFixupPointerInfo.swift │ │ │ ├── DyldChainedFixupsHeader.swift │ │ │ ├── DyldChainedImport.swift │ │ │ ├── DyldChainedImportFormat.swift │ │ │ ├── DyldChainedPage.swift │ │ │ ├── DyldChainedStartsInImage.swift │ │ │ ├── DyldChainedStartsInSegment.swift │ │ │ ├── DyldChainedStartsOffsets.swift │ │ │ └── DyldChainedSymbolsFormat.swift │ │ ├── Export │ │ │ ├── ExportSymbolFlags.swift │ │ │ ├── ExportSymbolKind.swift │ │ │ └── ExportTrieEntry.swift │ │ ├── ExportedSymbol.swift │ │ ├── FunctionStart.swift │ │ ├── IndirectSymbol.swift │ │ ├── Rebase.swift │ │ ├── Rebase │ │ │ ├── RebaseOpcode.swift │ │ │ ├── RebaseOperation.swift │ │ │ └── RebaseType.swift │ │ ├── Relocation │ │ │ ├── Relocation.swift │ │ │ ├── RelocationInfo.swift │ │ │ ├── RelocationLength.swift │ │ │ ├── RelocationType.swift │ │ │ └── ScatteredRelocationInfo.swift │ │ ├── StringTableEntry.swift │ │ └── Symbol │ │ │ ├── Nlist.swift │ │ │ ├── Stab.swift │ │ │ ├── SymbolDescription.swift │ │ │ ├── SymbolFlags.swift │ │ │ ├── SymbolLibraryOrdinalType.swift │ │ │ ├── SymbolReferenceFlag.swift │ │ │ └── SymbolType.swift │ ├── Protocol │ │ ├── CodeSignProtocol.swift │ │ ├── DyldCacheLocalSymbolsEntryProtocol.swift │ │ ├── DyldCacheRepresentable.swift │ │ ├── DyldChainedFixupsProtocol.swift │ │ ├── DyldChainedImportProtocol.swift │ │ ├── LayoutWrapper.swift │ │ ├── LoadCommandWrapper.swift │ │ ├── LoadCommandsProtocol.swift │ │ ├── MachORepresentable.swift │ │ ├── StringTable.swift │ │ └── SymbolProtocol.swift │ └── Util │ │ ├── BitFlags.swift │ │ ├── Sequence │ │ ├── DataSequence.swift │ │ └── MemorySequence.swift │ │ ├── SwiftDemangle.swift │ │ ├── TrieTree │ │ ├── DataTrieTree.swift │ │ ├── MemoryTrieTree.swift │ │ ├── Model │ │ │ └── TrieNode.swift │ │ └── Protocol │ │ │ ├── TrieNodeContent.swift │ │ │ └── TrieTreeProtocol.swift │ │ ├── exported.swift │ │ └── global.swift └── MachOKitC │ ├── include │ ├── backports.h │ ├── codesign.h │ ├── core_foundation.h │ ├── dyld_cache.h │ ├── dyld_cache_format.h │ ├── dyld_cache_loader.h │ ├── fixup-chains.h │ ├── mach-o.h │ ├── objc.h │ ├── swift.h │ └── thread_state.h │ ├── mach-o-linux.c │ ├── mach-o │ ├── fat.h │ ├── loader.h │ ├── nlist.h │ ├── reloc.h │ └── stab.h │ └── mach │ ├── machine.h │ └── vm_prot.h ├── Tests └── MachOKitTests │ ├── DyldCacheLoadedPrintTests.swift │ ├── DyldCachePrintTests.swift │ ├── MachOFilePrintTests.swift │ ├── MachOKitTests.swift │ └── MachOPrintTests.swift └── scripts ├── docc-preview.sh ├── docc.sh ├── generate-symbols.sh └── xcframework.sh /.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 | -------------------------------------------------------------------------------- /.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 15 | run: sudo xcode-select -s /Applications/Xcode_16.0.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 | -------------------------------------------------------------------------------- /.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.2.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.2.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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/ -------------------------------------------------------------------------------- /.spi.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | external_links: 3 | documentation: "https://p-x9.github.io/MachOKit/documentation/machokit/" 4 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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" : "23349fe1eb23c6ca2876d461a46ff60c0fa92f9c", 9 | "version" : "0.9.0" 10 | } 11 | } 12 | ], 13 | "version" : 2 14 | } 15 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/Extension/String+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/11/28. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | extension String { 12 | @_spi(Support) 13 | public typealias CCharTuple16 = (CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar) 14 | 15 | @_spi(Support) 16 | public init(tuple: CCharTuple16) { 17 | self = withUnsafePointer(to: tuple) { 18 | let size = MemoryLayout.size 19 | let data = Data(bytes: $0, count: size) + [0] 20 | return String(cString: data) ?? "" 21 | } 22 | } 23 | } 24 | 25 | extension String { 26 | @_spi(Support) 27 | public typealias CCharTuple32 = (CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar) 28 | 29 | @_spi(Support) 30 | public init(tuple: CCharTuple32) { 31 | self = withUnsafePointer(to: tuple) { 32 | let size = MemoryLayout.size 33 | let data = Data(bytes: $0, count: size) + [0] 34 | return String(cString: data) ?? "" 35 | } 36 | } 37 | } 38 | 39 | extension String { 40 | @_spi(Support) 41 | public init?(cString data: Data) { 42 | guard !data.isEmpty else { return nil } 43 | let string: String? = data.withUnsafeBytes { 44 | guard let baseAddress = $0.baseAddress else { return nil } 45 | let ptr = baseAddress.assumingMemoryBound(to: CChar.self) 46 | return String(cString: ptr) 47 | } 48 | guard let string else { 49 | return nil 50 | } 51 | self = string 52 | } 53 | } 54 | 55 | extension String { 56 | @inline(__always) 57 | func isEqual(to tuple: CCharTuple16) -> Bool { 58 | var buffer = tuple 59 | return withUnsafePointer(to: &buffer.0) { tuple in 60 | withCString { str in 61 | strcmp(str, tuple) == 0 62 | } 63 | } 64 | } 65 | 66 | @inline(__always) 67 | func isEqual(to tuple: CCharTuple32) -> Bool { 68 | var buffer = tuple 69 | return withUnsafePointer(to: &buffer.0) { tuple in 70 | withCString { str in 71 | strcmp(str, tuple) == 0 72 | } 73 | } 74 | } 75 | } 76 | 77 | @_spi(Support) 78 | public func == (string: String, tuple: String.CCharTuple16) -> Bool { 79 | string.isEqual(to: tuple) 80 | } 81 | 82 | @_spi(Support) 83 | public func == (tuple: String.CCharTuple16, string: String) -> Bool { 84 | string.isEqual(to: tuple) 85 | } 86 | 87 | @_spi(Support) 88 | public func == (string: String, tuple: String.CCharTuple32) -> Bool { 89 | string.isEqual(to: tuple) 90 | } 91 | 92 | @_spi(Support) 93 | public func == (tuple: String.CCharTuple32, string: String) -> Bool { 94 | string.isEqual(to: tuple) 95 | } 96 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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/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 { 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 | 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/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 { 12 | case development 13 | case production 14 | case multiCache 15 | } 16 | 17 | public enum DyldCacheSubType: UInt32 { 18 | case development 19 | case production 20 | } 21 | -------------------------------------------------------------------------------- /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 { 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/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 { 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/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 { 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/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 { 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/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, 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/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/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/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/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/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/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/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..: 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 { 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/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 { 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/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 = MachHeader.Flags( 24 | rawValue: Bit.weak_link.rawValue 25 | ) 26 | /// DYLIB_USE_REEXPORT 27 | public static let reexport = MachHeader.Flags( 28 | rawValue: Bit.reexport.rawValue 29 | ) 30 | /// DYLIB_USE_UPWARD 31 | public static let upward = MachHeader.Flags( 32 | rawValue: Bit.upward.rawValue 33 | ) 34 | /// DYLIB_USE_DELAYED_INIT 35 | public static let delayed_init = MachHeader.Flags( 36 | rawValue: Bit.delayed_init.rawValue 37 | ) 38 | } 39 | 40 | extension DylibUseFlags { 41 | public enum Bit: 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/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 { 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/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 { 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 { 20 | public typealias Layout = x86_thread_state64 21 | 22 | public var layout: Layout 23 | } 24 | 25 | public struct i386ThreadState: LayoutWrapper { 26 | public typealias Layout = i386_thread_state 27 | 28 | public var layout: Layout 29 | } 30 | 31 | public struct ARMThreadState: LayoutWrapper { 32 | public typealias Layout = arm_thread_state 33 | 34 | public var layout: Layout 35 | } 36 | 37 | public struct ARM64ThreadState: LayoutWrapper { 38 | public typealias Layout = arm_thread_state64 39 | 40 | public var layout: Layout 41 | } 42 | -------------------------------------------------------------------------------- /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 { 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/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 { 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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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 { 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/Bind/BindSpecial.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BindSpecial.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/03. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public enum BindSpecial { 12 | /// BIND_SPECIAL_DYLIB_SELF 13 | case dylib_self 14 | /// BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE 15 | case dylib_main_executable 16 | /// BIND_SPECIAL_DYLIB_FLAT_LOOKUP 17 | case dylib_flat_lookup 18 | /// BIND_SPECIAL_DYLIB_WEAK_LOOKUP 19 | case dylib_weak_lookup 20 | } 21 | 22 | extension BindSpecial: RawRepresentable { 23 | public typealias RawValue = Int32 24 | 25 | public init?(rawValue: RawValue) { 26 | switch rawValue { 27 | case BIND_SPECIAL_DYLIB_SELF: self = .dylib_self 28 | case BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE: self = .dylib_main_executable 29 | case BIND_SPECIAL_DYLIB_FLAT_LOOKUP: self = .dylib_flat_lookup 30 | case BIND_SPECIAL_DYLIB_WEAK_LOOKUP: self = .dylib_weak_lookup 31 | default: return nil 32 | } 33 | } 34 | 35 | public var rawValue: RawValue { 36 | switch self { 37 | case .dylib_self: BIND_SPECIAL_DYLIB_SELF 38 | case .dylib_main_executable: BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE 39 | case .dylib_flat_lookup: BIND_SPECIAL_DYLIB_FLAT_LOOKUP 40 | case .dylib_weak_lookup: BIND_SPECIAL_DYLIB_WEAK_LOOKUP 41 | } 42 | } 43 | } 44 | 45 | extension BindSpecial: CustomStringConvertible { 46 | public var description: String { 47 | switch self { 48 | case .dylib_self: "BIND_SPECIAL_DYLIB_SELF" 49 | case .dylib_main_executable: "BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE" 50 | case .dylib_flat_lookup: "BIND_SPECIAL_DYLIB_FLAT_LOOKUP" 51 | case .dylib_weak_lookup: "BIND_SPECIAL_DYLIB_WEAK_LOOKUP" 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /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 { 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/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 { 12 | case relocation(RelocationType) 13 | case pointer 14 | } 15 | -------------------------------------------------------------------------------- /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 { 12 | public let type: ClassicBindType 13 | public let address: UInt 14 | public let symbolIndex: Int 15 | public let addend: Int 16 | } 17 | -------------------------------------------------------------------------------- /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.fileSice.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/Model/Codesign/CodeDirectory/CodeSignCodeDirectory+executableSegment.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CodeSignCodeDirectory+executableSegment.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 executableSegment(in signature: MachOFile.CodeSign) -> ExecutableSegment? { 13 | guard isSupportsExecSegment else { 14 | return nil 15 | } 16 | let layout: CS_CodeDirectory_ExecSeg = signature.fileSice.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/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.fileSice.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.fileSice.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/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.fileSice.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 | -------------------------------------------------------------------------------- /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.fileSice.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.fileSice.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/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 { 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/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 { 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/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 { 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/Codesign/CodeSignMagic.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CodeSignMagic.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 CodeSignMagic { 13 | /// CSMAGIC_REQUIREMENT 14 | case requirement 15 | /// CSMAGIC_REQUIREMENTS 16 | case requirements 17 | /// CSMAGIC_CODEDIRECTORY 18 | case codedirectory 19 | /// CSMAGIC_EMBEDDED_SIGNATURE 20 | case embedded_signature 21 | /// CSMAGIC_EMBEDDED_SIGNATURE_OLD 22 | case embedded_signature_old 23 | /// CSMAGIC_EMBEDDED_ENTITLEMENTS 24 | case embedded_entitlements 25 | /// CSMAGIC_EMBEDDED_DER_ENTITLEMENTS 26 | case embedded_der_entitlements 27 | /// CSMAGIC_DETACHED_SIGNATURE 28 | case detached_signature 29 | /// CSMAGIC_BLOBWRAPPER 30 | case blobwrapper 31 | /// CSMAGIC_EMBEDDED_LAUNCH_CONSTRAINT 32 | case embedded_launch_constraint 33 | } 34 | 35 | extension CodeSignMagic: RawRepresentable { 36 | public typealias RawValue = UInt32 37 | 38 | public init?(rawValue: RawValue) { 39 | switch rawValue { 40 | case RawValue(CSMAGIC_REQUIREMENT): self = .requirement 41 | case RawValue(CSMAGIC_REQUIREMENTS): self = .requirements 42 | case RawValue(CSMAGIC_CODEDIRECTORY): self = .codedirectory 43 | case RawValue(CSMAGIC_EMBEDDED_SIGNATURE): self = .embedded_signature 44 | case RawValue(CSMAGIC_EMBEDDED_SIGNATURE_OLD): self = .embedded_signature_old 45 | case RawValue(CSMAGIC_EMBEDDED_ENTITLEMENTS): self = .embedded_entitlements 46 | case RawValue(CSMAGIC_EMBEDDED_DER_ENTITLEMENTS): self = .embedded_der_entitlements 47 | case RawValue(CSMAGIC_DETACHED_SIGNATURE): self = .detached_signature 48 | case RawValue(CSMAGIC_BLOBWRAPPER): self = .blobwrapper 49 | case RawValue(CSMAGIC_EMBEDDED_LAUNCH_CONSTRAINT): self = .embedded_launch_constraint 50 | default: return nil 51 | } 52 | } 53 | 54 | public var rawValue: RawValue { 55 | switch self { 56 | case .requirement: RawValue(CSMAGIC_REQUIREMENT) 57 | case .requirements: RawValue(CSMAGIC_REQUIREMENTS) 58 | case .codedirectory: RawValue(CSMAGIC_CODEDIRECTORY) 59 | case .embedded_signature: RawValue(CSMAGIC_EMBEDDED_SIGNATURE) 60 | case .embedded_signature_old: RawValue(CSMAGIC_EMBEDDED_SIGNATURE_OLD) 61 | case .embedded_entitlements: RawValue(CSMAGIC_EMBEDDED_ENTITLEMENTS) 62 | case .embedded_der_entitlements: RawValue(CSMAGIC_EMBEDDED_DER_ENTITLEMENTS) 63 | case .detached_signature: RawValue(CSMAGIC_DETACHED_SIGNATURE) 64 | case .blobwrapper: RawValue(CSMAGIC_BLOBWRAPPER) 65 | case .embedded_launch_constraint: RawValue(CSMAGIC_EMBEDDED_LAUNCH_CONSTRAINT) 66 | } 67 | } 68 | } 69 | 70 | extension CodeSignMagic: CustomStringConvertible { 71 | public var description: String { 72 | switch self { 73 | case .requirement: "CSMAGIC_REQUIREMENT" 74 | case .requirements: "CSMAGIC_REQUIREMENTS" 75 | case .codedirectory: "CSMAGIC_CODEDIRECTORY" 76 | case .embedded_signature: "CSMAGIC_EMBEDDED_SIGNATURE" 77 | case .embedded_signature_old: "CSMAGIC_EMBEDDED_SIGNATURE_OLD" 78 | case .embedded_entitlements: "CSMAGIC_EMBEDDED_ENTITLEMENTS" 79 | case .embedded_der_entitlements: "CSMAGIC_EMBEDDED_DER_ENTITLEMENTS" 80 | case .detached_signature: "CSMAGIC_DETACHED_SIGNATURE" 81 | case .blobwrapper: "CSMAGIC_BLOBWRAPPER" 82 | case .embedded_launch_constraint: "CSMAGIC_EMBEDDED_LAUNCH_CONSTRAINT" 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /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 { 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/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 { 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.fileSice.readData( 41 | offset: offset, 42 | upToCount: signature.fileSice.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/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 { 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 { 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/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 { 12 | public enum DependType { 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/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 { 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 | -------------------------------------------------------------------------------- /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 { 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/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 { 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 | guard let offset = cache.fileOffset(of: numericCast(address)) else { 34 | return nil 35 | } 36 | return cache.fileHandle.readDataSequence( 37 | offset: offset + numericCast(layoutOffset(of: \.entries)), 38 | numberOfElements: numericCast(layout.count) 39 | ) 40 | } 41 | } 42 | 43 | extension DyldCacheFunctionVariantInfo { 44 | public var size: Int { 45 | layoutSize + DyldCacheFunctionVariantEntry.layoutSize * numericCast(layout.count) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /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 { 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 | cache.fileHandle.readString( 23 | offset: numericCast(layout.pathFileOffset) 24 | ) 25 | } 26 | 27 | /// Path for image 28 | /// - Parameter cache: DyldCache to which this image belongs 29 | /// - Returns: Path for image 30 | public func path(in cache: DyldCacheLoaded) -> String? { 31 | String( 32 | cString: cache.ptr 33 | .advanced(by: numericCast(layout.pathFileOffset)) 34 | .assumingMemoryBound(to: CChar.self) 35 | ) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /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 { 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 | cache.fileHandle.readString( 28 | offset: numericCast(layout.pathOffset) 29 | ) 30 | } 31 | 32 | /// Path for image text 33 | /// - Parameter cache: DyldCache to which this image belongs 34 | /// - Returns: Path for image text 35 | public func path(in cache: DyldCacheLoaded) -> String? { 36 | String( 37 | cString: cache.ptr 38 | .advanced(by: numericCast(layout.pathOffset)) 39 | .assumingMemoryBound(to: CChar.self) 40 | ) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /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 { 12 | public typealias Layout = dyld_cache_local_symbols_entry 13 | 14 | public var layout: Layout 15 | } 16 | 17 | extension DyldCacheLocalSymbolsEntry: DyldCacheLocalSymbolsEntryProtocol { 18 | public var dylibOffset: Int { 19 | numericCast(layout.dylibOffset) 20 | } 21 | 22 | public var nlistStartIndex: Int { 23 | numericCast(layout.nlistStartIndex) 24 | } 25 | 26 | public var nlistCount: Int { 27 | numericCast(layout.nlistCount) 28 | } 29 | } 30 | 31 | public struct DyldCacheLocalSymbolsEntry64: LayoutWrapper { 32 | public typealias Layout = dyld_cache_local_symbols_entry_64 33 | 34 | public var layout: Layout 35 | } 36 | 37 | extension DyldCacheLocalSymbolsEntry64: DyldCacheLocalSymbolsEntryProtocol { 38 | public var dylibOffset: Int { 39 | numericCast(layout.dylibOffset) 40 | } 41 | 42 | public var nlistStartIndex: Int { 43 | numericCast(layout.nlistStartIndex) 44 | } 45 | 46 | public var nlistCount: Int { 47 | numericCast(layout.nlistCount) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /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 { 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 | -------------------------------------------------------------------------------- /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 { 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 | let offset = offset + layoutOffset(of: \.entries) 33 | let sharedRegionStart = cache.mainCacheHeader.sharedRegionStart 34 | guard let resolvedOffset = cache.fileOffset( 35 | of: sharedRegionStart + numericCast(offset) 36 | ) else { 37 | return nil 38 | } 39 | return cache.fileHandle.readDataSequence( 40 | offset: resolvedOffset, 41 | numberOfElements: numericCast(layout.count) 42 | ) 43 | } 44 | } 45 | 46 | extension DyldCachePrewarming { 47 | public var size: Int { 48 | layoutSize + DyldCachePrewarmingEntry.layoutSize * numericCast(layout.count) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /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 { 13 | public typealias Layout = dyld_prewarming_entry 14 | 15 | public var layout: Layout 16 | } 17 | -------------------------------------------------------------------------------- /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 { 12 | public typealias Layout = dyld_cache_tpro_mapping_info 13 | 14 | public var layout: Layout 15 | } 16 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldCache/DyldSubCacheEntry.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldSubCacheEntry.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/01/15. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public enum DyldSubCacheEntryType { 12 | case general 13 | case v1 14 | 15 | var layoutSize: Int { 16 | switch self { 17 | case .general: 18 | DyldSubCacheEntryGeneral.layoutSize 19 | case .v1: 20 | DyldSubCacheEntryV1.layoutSize 21 | } 22 | } 23 | } 24 | 25 | public enum DyldSubCacheEntry { 26 | case general(DyldSubCacheEntryGeneral) 27 | case v1(DyldSubCacheEntryV1) 28 | 29 | public var type: DyldSubCacheEntryType { 30 | switch self { 31 | case .general: .general 32 | case .v1: .v1 33 | } 34 | } 35 | 36 | /// UUID of sub cache 37 | public var uuid: UUID { 38 | switch self { 39 | case let .general(info): info.uuid 40 | case let .v1(info): info.uuid 41 | } 42 | } 43 | 44 | /// Offset of this subcache from the main cache base address 45 | public var cacheVMOffset: UInt64 { 46 | switch self { 47 | case let .general(info): info.cacheVMOffset 48 | case let .v1(info): info.cacheVMOffset 49 | } 50 | } 51 | 52 | /// File name suffix of the subCache file 53 | /// 54 | /// e.g. ".25.data", ".03.development" 55 | public var fileSuffix: String { 56 | switch self { 57 | case let .general(info): info.fileSuffix 58 | case let .v1(info): info.fileSuffix 59 | } 60 | } 61 | } 62 | 63 | // cache 64 | extension DyldSubCacheEntry { 65 | public func subcache(for cache: DyldCache) throws -> DyldCache? { 66 | let url = URL(fileURLWithPath: cache.url.path + fileSuffix) 67 | return try DyldCache(subcacheUrl: url, mainCacheHeader: cache.header) 68 | } 69 | 70 | public func subcache(for cache: DyldCacheLoaded) throws -> DyldCacheLoaded? { 71 | try DyldCacheLoaded( 72 | subcachePtr: cache.ptr 73 | .advanced(by: numericCast(cacheVMOffset)), 74 | mainCacheHeader: cache.header 75 | ) 76 | } 77 | } 78 | 79 | public struct DyldSubCacheEntryV1: LayoutWrapper { 80 | public typealias Layout = dyld_subcache_entry_v1 81 | 82 | public var layout: Layout 83 | public let index: Int 84 | } 85 | 86 | extension DyldSubCacheEntryV1 { 87 | /// UUID of sub cache 88 | public var uuid: UUID { 89 | .init(uuid: layout.uuid) 90 | } 91 | 92 | /// File name suffix of the subCache file 93 | /// 94 | /// e.g. ".01", ".02" 95 | public var fileSuffix: String { 96 | "." + String(format: "%02d", index) 97 | } 98 | } 99 | 100 | public struct DyldSubCacheEntryGeneral: LayoutWrapper { 101 | public typealias Layout = dyld_subcache_entry 102 | 103 | public var layout: Layout 104 | public let index: Int 105 | } 106 | 107 | extension DyldSubCacheEntryGeneral { 108 | /// UUID of sub cache 109 | public var uuid: UUID { 110 | .init(uuid: layout.uuid) 111 | } 112 | 113 | /// File name suffix of the subCache file 114 | /// 115 | /// e.g. ".25.data", ".03.development" 116 | public var fileSuffix: String { 117 | .init(tuple: layout.fileSuffix) 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /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 { 15 | // Dylib name 16 | public let name: String 17 | /// Dylib index 18 | public let index: UInt32 19 | } 20 | -------------------------------------------------------------------------------- /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 { 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/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 { 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/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 { 12 | public typealias Layout = objc_binary_info 13 | 14 | public var layout: Layout 15 | } 16 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldCache/Loader/PrebuiltLoaderProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PrebuiltLoaderProtocol.swift 3 | // MachOKit 4 | // 5 | // Created by p-x9 on 2024/11/09 6 | // 7 | // 8 | 9 | 10 | import Foundation 11 | 12 | public protocol PrebuiltLoaderProtocol { 13 | /// Address where this loader is located. 14 | /// 15 | /// Slides after loading are not included. 16 | var address: Int { get } 17 | 18 | /// magic of loader starts 19 | var magic: String? { get } 20 | /// PrebuiltLoader vs JustInTimeLoader 21 | var isPrebuilt: Bool { get } 22 | var neverUnload: Bool { get } 23 | var isPremapped: Bool { get } 24 | 25 | var ref: LoaderRef { get } 26 | 27 | // Information for all pre-calculated sections that we know about 28 | var sectionLocations: SectionLocations { get } 29 | 30 | /// path for target mach-o image 31 | /// - Parameter cache: DyldCache to which `self` belongs 32 | /// - Returns: path name 33 | func path(in cache: DyldCache) -> String? 34 | /// alternative path for target mach-o image if install_name does not match real path 35 | /// - Parameter cache: DyldCache to which `self` belongs 36 | /// - Returns: path name 37 | func altPath(in cache: DyldCache) -> String? 38 | /// loader reference list of target 's dependencies 39 | /// - Parameter cache: DyldCache to which `self` belongs 40 | /// - Returns: sequence of loader reference 41 | func dependentLoaderRefs(in cache: DyldCache) -> DataSequence? 42 | /// Stores information about the layout of the objc sections in a binary 43 | /// - Parameter cache: DyldCache to which `self` belongs 44 | /// - Returns: binary info for objc 45 | func objcBinaryInfo(in cache: DyldCache) -> ObjCBinaryInfo? 46 | 47 | /// path for target mach-o image 48 | /// - Parameter cache: DyldCacheLoaded to which `self` belongs 49 | /// - Returns: path name 50 | func path(in cache: DyldCacheLoaded) -> String? 51 | /// alternative path for target mach-o image if install_name does not match real path 52 | /// - Parameter cache: DyldCacheLoaded to which `self` belongs 53 | /// - Returns: path name 54 | func altPath(in cache: DyldCacheLoaded) -> String? 55 | /// loader reference list of target 's dependencies 56 | /// - Parameter cache: DyldCacheLoaded to which `self` belongs 57 | /// - Returns: sequence of loader reference 58 | func dependentLoaderRefs(in cache: DyldCacheLoaded) -> MemorySequence? 59 | /// Stores information about the layout of the objc sections in a binary 60 | /// - Parameter cache: DyldCacheLoaded to which `self` belongs 61 | /// - Returns: binary info for objc 62 | func objcBinaryInfo(in cache: DyldCacheLoaded) -> ObjCBinaryInfo? 63 | } 64 | -------------------------------------------------------------------------------- /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 { 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, 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 { 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/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 { 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/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 { 12 | public let name: String 13 | public let offset: UInt32 14 | } 15 | -------------------------------------------------------------------------------- /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 { 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/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 { 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 { 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 | -------------------------------------------------------------------------------- /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 { 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 | guard layout.toc_offset > 0 else { return nil } 34 | return cache.fileHandle.readDataSequence( 35 | offset: numericCast(offset) + numericCast(layout.toc_offset), 36 | numberOfElements: numberOfTableContents 37 | ) 38 | } 39 | } 40 | 41 | extension DyldCacheSlideInfo1 { 42 | public var numberOfEntries: Int { 43 | numericCast(layout.entries_count) 44 | } 45 | 46 | public func entries(in cache: DyldCache) -> DataSequence? { 47 | precondition(layout.entries_size == Entry.layoutSize) 48 | guard layout.entries_offset > 0 else { return nil } 49 | return cache.fileHandle.readDataSequence( 50 | offset: numericCast(offset) + numericCast(layout.entries_offset), 51 | numberOfElements: numberOfEntries 52 | ) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/DyldCache/SlideInfo/DyldCacheSlideInfo2.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DyldCacheSlideInfo2.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 DyldCacheSlideInfo2: LayoutWrapper { 13 | public typealias Layout = dyld_cache_slide_info2 14 | 15 | public var layout: Layout 16 | public var offset: Int 17 | } 18 | 19 | // MARK: - PageStart 20 | extension DyldCacheSlideInfo2 { 21 | public struct PageStart { 22 | public let value: UInt16 23 | 24 | public var isNoRebase: Bool { 25 | value == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE 26 | } 27 | 28 | public var isExtra: Bool { 29 | (value & UInt16(DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA)) > 0 30 | } 31 | 32 | public var extrasStartIndex: Int? { 33 | guard isExtra else { return nil } 34 | return numericCast(value & ~UInt16(DYLD_CACHE_SLIDE_PAGE_ATTRS)) 35 | } 36 | } 37 | } 38 | 39 | // MARK: - PageExtra 40 | extension DyldCacheSlideInfo2 { 41 | public struct PageExtra { 42 | public let value: UInt16 43 | 44 | public var isEnd: Bool { 45 | (value & UInt16(DYLD_CACHE_SLIDE_PAGE_ATTR_END)) > 0 46 | } 47 | } 48 | } 49 | 50 | // MARK: - function & proerty 51 | extension DyldCacheSlideInfo2 { 52 | public var pageSize: Int { 53 | numericCast(layout.page_size) 54 | } 55 | } 56 | 57 | extension DyldCacheSlideInfo2 { 58 | public var numberOfPageStarts: Int { 59 | numericCast(layout.page_starts_count) 60 | } 61 | 62 | public func pageStarts(in cache: DyldCache) -> DataSequence? { 63 | guard layout.page_starts_offset > 0 else { return nil } 64 | return cache.fileHandle.readDataSequence( 65 | offset: numericCast(offset) + numericCast(layout.page_starts_offset), 66 | numberOfElements: numberOfPageStarts 67 | ) 68 | } 69 | } 70 | 71 | extension DyldCacheSlideInfo2 { 72 | public var numberOfPageExtras: Int { 73 | numericCast(layout.page_extras_count) 74 | } 75 | 76 | public func pageExtras(in cache: DyldCache) -> DataSequence? { 77 | guard layout.page_extras_offset > 0 else { return nil } 78 | return cache.fileHandle.readDataSequence( 79 | offset: numericCast(offset) + numericCast(layout.page_extras_offset), 80 | numberOfElements: numberOfPageExtras 81 | ) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /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 { 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 | let pageStartsOffset = layoutSize 44 | return cache.fileHandle.readDataSequence( 45 | offset: numericCast(offset) + numericCast(pageStartsOffset), 46 | numberOfElements: numberOfPageStarts 47 | ) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /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 { 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 | cache.fileHandle.readDataSequence( 64 | offset: numericCast(offset) + numericCast(layout.page_starts_offset), 65 | numberOfElements: numberOfPageStarts 66 | ) 67 | } 68 | } 69 | 70 | extension DyldCacheSlideInfo4 { 71 | public var numberOfPageExtras: Int { 72 | numericCast(layout.page_extras_count) 73 | } 74 | 75 | public func pageExtras(in cache: DyldCache) -> DataSequence? { 76 | guard layout.page_extras_offset > 0 else { return nil } 77 | return cache.fileHandle.readDataSequence( 78 | offset: numericCast(offset) + numericCast(layout.page_extras_offset), 79 | numberOfElements: numberOfPageExtras 80 | ) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /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 { 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 | let pageStartsOffset = layoutSize 44 | return cache.fileHandle.readDataSequence( 45 | offset: numericCast(offset) + numericCast(pageStartsOffset), 46 | numberOfElements: numberOfPageStarts 47 | ) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /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 { 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/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 { 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/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 { 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/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 { 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/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 { 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/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 { 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/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 { 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/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 { 12 | case uncompressed 13 | case zlibCompressed 14 | } 15 | -------------------------------------------------------------------------------- /Sources/MachOKit/Model/Export/ExportSymbolFlags.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExportSymbolFlags.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/03. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public struct ExportSymbolFlags: BitFlags { 12 | public typealias RawValue = Int32 13 | 14 | public let rawValue: RawValue 15 | 16 | public var kind: ExportSymbolKind? { 17 | .init(rawValue: rawValue & EXPORT_SYMBOL_FLAGS_KIND_MASK) 18 | } 19 | 20 | public init(rawValue: RawValue) { 21 | self.rawValue = rawValue 22 | } 23 | } 24 | 25 | extension ExportSymbolFlags { 26 | /// EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION 27 | public static let weak_definition = ExportSymbolFlags( 28 | rawValue: Bit.weak_definition.rawValue 29 | ) 30 | /// EXPORT_SYMBOL_FLAGS_REEXPORT 31 | public static let reexport = ExportSymbolFlags( 32 | rawValue: Bit.reexport.rawValue 33 | ) 34 | /// EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 35 | public static let stub_and_resolver = ExportSymbolFlags( 36 | rawValue: Bit.stub_and_resolver.rawValue 37 | ) 38 | /// EXPORT_SYMBOL_FLAGS_STATIC_RESOLVER 39 | public static let static_resolver = ExportSymbolFlags( 40 | rawValue: Bit.static_resolver.rawValue 41 | ) 42 | } 43 | 44 | extension ExportSymbolFlags { 45 | public enum Bit: CaseIterable { 46 | /// EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION 47 | case weak_definition 48 | /// EXPORT_SYMBOL_FLAGS_REEXPORT 49 | case reexport 50 | /// EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 51 | case stub_and_resolver 52 | /// EXPORT_SYMBOL_FLAGS_STATIC_RESOLVER 53 | case static_resolver 54 | } 55 | } 56 | 57 | extension ExportSymbolFlags.Bit: RawRepresentable { 58 | public typealias RawValue = Int32 59 | 60 | public init?(rawValue: RawValue) { 61 | switch rawValue { 62 | case RawValue(EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION): self = .weak_definition 63 | case RawValue(EXPORT_SYMBOL_FLAGS_REEXPORT): self = .reexport 64 | case RawValue(EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER): self = .stub_and_resolver 65 | case RawValue(EXPORT_SYMBOL_FLAGS_STATIC_RESOLVER): self = .static_resolver 66 | default: return nil 67 | } 68 | } 69 | 70 | public var rawValue: RawValue { 71 | switch self { 72 | case .weak_definition: RawValue(EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION) 73 | case .reexport: RawValue(EXPORT_SYMBOL_FLAGS_REEXPORT) 74 | case .stub_and_resolver: RawValue(EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) 75 | case .static_resolver: RawValue(EXPORT_SYMBOL_FLAGS_STATIC_RESOLVER) 76 | } 77 | } 78 | } 79 | 80 | extension ExportSymbolFlags.Bit: CustomStringConvertible { 81 | public var description: String { 82 | switch self { 83 | case .weak_definition: "EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION" 84 | case .reexport: "EXPORT_SYMBOL_FLAGS_REEXPORT" 85 | case .stub_and_resolver: "EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER" 86 | case .static_resolver: "EXPORT_SYMBOL_FLAGS_STATIC_RESOLVER" 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /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 { 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/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 { 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 | 26 | extension ExportedSymbol { 27 | // [dyld implementation](https://github.com/apple-oss-distributions/dyld/blob/66c652a1f1f6b7b5266b8bbfd51cb0965d67cc44/common/MachOLoaded.cpp#L258) 28 | public func resolver(for machO: MachOImage) -> (@convention(c) () -> UInt)? { 29 | guard let resolverOffset else { return nil } 30 | return autoBitCast( 31 | machO.ptr.advanced(by: numericCast(resolverOffset)) 32 | ) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /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 { 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/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 { 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/Model/Rebase/RebaseOpcode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Rebase.swift 3 | // 4 | // 5 | // Created by p-x9 on 2023/12/02. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public enum RebaseOpcode { 12 | /// REBASE_OPCODE_DONE 13 | case done 14 | /// REBASE_OPCODE_SET_TYPE_IMM 15 | case set_type_imm 16 | /// REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB 17 | case set_segment_and_offset_uleb 18 | /// REBASE_OPCODE_ADD_ADDR_ULEB 19 | case add_addr_uleb 20 | /// REBASE_OPCODE_ADD_ADDR_IMM_SCALED 21 | case add_addr_imm_scaled 22 | /// REBASE_OPCODE_DO_REBASE_IMM_TIMES 23 | case do_rebase_imm_times 24 | /// REBASE_OPCODE_DO_REBASE_ULEB_TIMES 25 | case do_rebase_uleb_times 26 | /// REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB 27 | case do_rebase_add_addr_uleb 28 | /// REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB 29 | case do_rebase_uleb_times_skipping_uleb 30 | } 31 | 32 | extension RebaseOpcode: RawRepresentable { 33 | public typealias RawValue = Int32 34 | 35 | public init?(rawValue: RawValue) { 36 | switch rawValue { 37 | case REBASE_OPCODE_DONE: self = .done 38 | case REBASE_OPCODE_SET_TYPE_IMM: self = .set_type_imm 39 | case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: self = .set_segment_and_offset_uleb 40 | case REBASE_OPCODE_ADD_ADDR_ULEB: self = .add_addr_uleb 41 | case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: self = .add_addr_imm_scaled 42 | case REBASE_OPCODE_DO_REBASE_IMM_TIMES: self = .do_rebase_imm_times 43 | case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: self = .do_rebase_uleb_times 44 | case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: self = .do_rebase_add_addr_uleb 45 | case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: self = .do_rebase_uleb_times_skipping_uleb 46 | default: return nil 47 | } 48 | } 49 | 50 | public var rawValue: RawValue { 51 | switch self { 52 | case .done: REBASE_OPCODE_DONE 53 | case .set_type_imm: REBASE_OPCODE_SET_TYPE_IMM 54 | case .set_segment_and_offset_uleb: REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB 55 | case .add_addr_uleb: REBASE_OPCODE_ADD_ADDR_ULEB 56 | case .add_addr_imm_scaled: REBASE_OPCODE_ADD_ADDR_IMM_SCALED 57 | case .do_rebase_imm_times: REBASE_OPCODE_DO_REBASE_IMM_TIMES 58 | case .do_rebase_uleb_times: REBASE_OPCODE_DO_REBASE_ULEB_TIMES 59 | case .do_rebase_add_addr_uleb: REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB 60 | case .do_rebase_uleb_times_skipping_uleb: REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB 61 | } 62 | } 63 | } 64 | 65 | extension RebaseOpcode: CustomStringConvertible { 66 | public var description: String { 67 | switch self { 68 | case .done: "REBASE_OPCODE_DONE" 69 | case .set_type_imm: "REBASE_OPCODE_SET_TYPE_IMM" 70 | case .set_segment_and_offset_uleb: "REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB" 71 | case .add_addr_uleb: "REBASE_OPCODE_ADD_ADDR_ULEB" 72 | case .add_addr_imm_scaled: "REBASE_OPCODE_ADD_ADDR_IMM_SCALED" 73 | case .do_rebase_imm_times: "REBASE_OPCODE_DO_REBASE_IMM_TIMES" 74 | case .do_rebase_uleb_times: "REBASE_OPCODE_DO_REBASE_ULEB_TIMES" 75 | case .do_rebase_add_addr_uleb: "REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB" 76 | case .do_rebase_uleb_times_skipping_uleb: "REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB" 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /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 { 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/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 { 12 | public enum Info { 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/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 { 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/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 { 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/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 { 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/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 { 12 | public let string: String 13 | /// Offset from the beginning of the string table 14 | public let offset: Int 15 | } 16 | -------------------------------------------------------------------------------- /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 { 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/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: 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/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 { 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/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 { 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/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 { 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/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 { 12 | /// Offset in cache file 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 | -------------------------------------------------------------------------------- /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/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 { 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/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/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/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 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 { 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/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/Util/TrieTree/Model/TrieNode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TrieNode.swift 3 | // 4 | // 5 | // Created by p-x9 on 2024/07/05 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | @dynamicMemberLookup 12 | public struct TrieNode { 13 | public struct Child { 14 | public let label: String 15 | public let offset: UInt 16 | } 17 | 18 | public let offset: Int 19 | public let terminalSize: UInt 20 | 21 | // Content 22 | public var content: Content? 23 | 24 | public var children: [Child] 25 | 26 | @_spi(Support) 27 | public init( 28 | offset: Int, 29 | terminalSize: UInt, 30 | content: Content?, 31 | children: [Child] 32 | ) { 33 | self.offset = offset 34 | self.terminalSize = terminalSize 35 | self.content = content 36 | self.children = children 37 | } 38 | } 39 | 40 | extension TrieNode { 41 | public var isTerminal: Bool { 42 | terminalSize != 0 43 | } 44 | } 45 | 46 | extension TrieNode { 47 | public static func readNext( 48 | basePointer: UnsafePointer, 49 | trieSize: Int, 50 | nextOffset: inout Int 51 | ) -> Self? { 52 | guard nextOffset < trieSize else { return nil } 53 | 54 | let (terminalSize, terminalBytes) = basePointer 55 | .advanced(by: nextOffset) 56 | .readULEB128() 57 | nextOffset += terminalBytes 58 | 59 | var entry = TrieNode( 60 | offset: nextOffset - terminalBytes, 61 | terminalSize: terminalSize, 62 | content: nil, 63 | children: [] 64 | ) 65 | 66 | var childrenOffset = nextOffset + Int(terminalSize) 67 | 68 | if terminalSize != 0 { 69 | entry.content = .read( 70 | basePointer: basePointer, 71 | trieSize: trieSize, 72 | nextOffset: &nextOffset 73 | ) 74 | } 75 | 76 | guard childrenOffset < trieSize else { return entry } 77 | 78 | let numberOfChildren = basePointer 79 | .advanced(by: childrenOffset) 80 | .pointee 81 | childrenOffset += MemoryLayout.size 82 | 83 | for _ in 0..(dynamicMember keyPath: KeyPath) -> Value? { 106 | content?[keyPath: keyPath] 107 | } 108 | 109 | public subscript(dynamicMember keyPath: KeyPath) -> Value? { 110 | content?[keyPath: keyPath] 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /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/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/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/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 | #endif /* backports_h */ 85 | -------------------------------------------------------------------------------- /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/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 | #ifndef __linux__ 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 | 20 | #endif 21 | 22 | #endif /* dyld_cache_h */ 23 | -------------------------------------------------------------------------------- /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/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/MachOKitC/mach-o/fat.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Apple, Inc. All rights reserved. 3 | * 4 | * @APPLE_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apple Public Source License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://www.opensource.apple.com/apsl/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPLE_LICENSE_HEADER_END@ 22 | */ 23 | #ifndef _MACH_O_FAT_H_ 24 | #define _MACH_O_FAT_H_ 25 | /* 26 | * This header file describes the structures of the file format for "fat" 27 | * architecture specific file (wrapper design). At the begining of the file 28 | * there is one fat_header structure followed by a number of fat_arch 29 | * structures. For each architecture in the file, specified by a pair of 30 | * cputype and cpusubtype, the fat_header describes the file offset, file 31 | * size and alignment in the file of the architecture specific member. 32 | * The padded bytes in the file to place each member on it's specific alignment 33 | * are defined to be read as zeros and can be left as "holes" if the file system 34 | * can support them as long as they read as zeros. 35 | * 36 | * All structures defined here are always written and read to/from disk 37 | * in big-endian order. 38 | */ 39 | 40 | /* 41 | * is needed here for the cpu_type_t and cpu_subtype_t types 42 | * and contains the constants for the possible values of these types. 43 | */ 44 | #include 45 | #include "../mach/machine.h" 46 | //#include 47 | 48 | #define FAT_MAGIC 0xcafebabe 49 | #define FAT_CIGAM 0xbebafeca /* NXSwapLong(FAT_MAGIC) */ 50 | 51 | struct fat_header { 52 | uint32_t magic; /* FAT_MAGIC or FAT_MAGIC_64 */ 53 | uint32_t nfat_arch; /* number of structs that follow */ 54 | }; 55 | 56 | struct fat_arch { 57 | int32_t cputype; /* cpu specifier (int) */ 58 | int32_t cpusubtype; /* machine specifier (int) */ 59 | uint32_t offset; /* file offset to this object file */ 60 | uint32_t size; /* size of this object file */ 61 | uint32_t align; /* alignment as a power of 2 */ 62 | }; 63 | 64 | /* 65 | * The support for the 64-bit fat file format described here is a work in 66 | * progress and not yet fully supported in all the Apple Developer Tools. 67 | * 68 | * When a slice is greater than 4mb or an offset to a slice is greater than 4mb 69 | * then the 64-bit fat file format is used. 70 | */ 71 | #define FAT_MAGIC_64 0xcafebabf 72 | #define FAT_CIGAM_64 0xbfbafeca /* NXSwapLong(FAT_MAGIC_64) */ 73 | 74 | struct fat_arch_64 { 75 | int32_t cputype; /* cpu specifier (int) */ 76 | int32_t cpusubtype; /* machine specifier (int) */ 77 | uint64_t offset; /* file offset to this object file */ 78 | uint64_t size; /* size of this object file */ 79 | uint32_t align; /* alignment as a power of 2 */ 80 | uint32_t reserved; /* reserved */ 81 | }; 82 | 83 | #endif /* _MACH_O_FAT_H_ */ 84 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------