[PR #953] [MERGED] feat(native): decode chained fixups, binds, ObjC categories and properties #2531

Closed
opened 2026-10-17 21:07:14 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/953
Author: @kaoru0822-kitauji
Created: 10/7/2026
Status: ✅ Merged
Merged: 10/7/2026
Merged by: @kaoru0822-kitauji

Base: main ← Head: feat/macho-chained-fixups


📝 Commits (1)

  • 3e24a72 feat(native): decode chained fixups, binds, ObjC categories and properties

📊 Changes

19 files changed (+1464 additions, -239 deletions)

View changed files

📝 docs/native-investigation.md (+18 -5)
📝 docs/product-catalog.json (+2 -2)
📝 docs/testing.md (+18 -11)
📝 docs/verification/managed-conformance-manifest.json (+2 -2)
📝 scripts/verify-apple-dispatch.mjs (+181 -109)
📝 skills/reverse-engineer-anything/SKILL.md (+1 -1)
📝 src/domain/nativeInvestigationGraph.test.ts (+3 -0)
📝 src/domain/objcSwiftMetadata.ts (+18 -0)
📝 src/generatedMcpToolCatalog.ts (+1 -1)
➕ src/native/AppleDispatchCoverage.ts (+82 -0)
📝 src/native/AppleDispatchMetadata.ts (+30 -43)
➕ src/native/AppleMachoFixups.test.ts (+275 -0)
➕ src/native/AppleMachoFixups.ts (+414 -0)
📝 src/native/AppleMachoSelection.ts (+131 -38)
➕ src/native/AppleObjcCategories.ts (+99 -0)
📝 src/native/AppleObjcDispatchFacets.ts (+70 -25)
➕ src/native/AppleObjcProperties.ts (+103 -0)
📝 src/native/AppleObjcProtocols.ts (+8 -2)
📝 tests/conformance/native/dispatch.m (+8 -0)

📄 Description

Summary

inspect_native_dispatch_metadata read Mach-O pointer fields as stored bytes.

  • Chained-fixup images, the linker default since Xcode 13, store fixup chains rather than addresses, so their class lists decoded nothing.
  • Superclasses defined in other images (NSObject, NSViewController, …) were always reported as superclass_binding_unresolved.

This PR decodes pointer fixups before reading Objective-C metadata. It also adds the property and category records that the coverage facet previously marked unsupported.

Fixups (src/native/AppleMachoFixups.ts)

  • LC_DYLD_CHAINED_FIXUPS
    • Supported pointer formats: DYLD_CHAINED_PTR_64, 64_OFFSET, ARM64E, ARM64E_USERLAND and ARM64E_USERLAND24, covering plain and authenticated rebases and binds.
    • Supported import formats: DYLD_CHAINED_IMPORT, _ADDEND and _ADDEND64.
    • Compressed symbol tables and unknown pointer formats are rejected with the format named. The decoder does not guess for them.
  • LC_DYLD_INFO(_ONLY): the bind, weak-bind and lazy-bind opcode streams are interpreted as an address-to-import map. Stream failures are reported in coverage and do not abort the decode.

Objective-C

  • An external superclass is named from its _OBJC_CLASS_$_Name bind. superclass_address stays null for these because the class is not in the image.
  • class_ro_t.baseProperties is decoded into objc_classes[].properties, giving each property's name, type encoding, attributes and getter/setter.
  • __objc_catlist category_t records become the new objc_categories result.
    • Each record has the category's name, its class, and class_source, which is external (bound), local or unresolved.
    • It also lists instance and class method selectors, protocols, properties, location and Evidence.
    • Category methods appear in objc_dispatch_implementations with a category field.
  • Protocol-list counts are read as plain data, not through fixup decoding.

Coverage

  • objc_properties_categories is now decoded and reports partial status with reasons. It was previously unsupported.
  • New pointer_fixups facet reports which encoding was used, for example chained fixups: DYLD_CHAINED_PTR_64_OFFSET, LC_DYLD_INFO bind opcodes or no fixup load commands, plus any failures.
  • The Swift generic/resilient facet is still unsupported. Swift __swift5_fieldmd/__swift5_reflstr and selector-ref projection are not in this PR.

Contract changes (additive)

  • objc_categories: ObjcCategory[] is added to the dispatch metadata result. It is always present and empty when the image has no categories.
  • objc_dispatch_implementations[].category?: string | null is added.
  • There are two new or changed coverage facets: objc_properties_categories is now decoded, and pointer_fixups is new.

CLI excerpt from rea inspect-native-dispatch-metadata on the conformance fixture (tests/conformance/native/dispatch.m, default chained-fixup link, arm64). Paths and digests are trimmed:

objc_classes[2]:
  - name: ReaDispatchFixture
    super_class: NSObject          # from the _OBJC_CLASS_$_NSObject bind
    properties[1]:
      - name: fixtureName
        type_encoding: "@\"NSString\""
        attributes[4]{name,value,...}: T,"@\"NSString\""  C,""  N,""  V,_fixtureName
    superclass_address: null
    decode: { status: decoded, reason: null }
objc_categories[1]:
  - name: ReaDispatchAdditions
    class_name: NSString
    class_source: external
    instance_methods[1]: reaFixtureLength
coverage:
  objc_properties_categories,complete,null,1,1
  pointer_fixups,complete,"chained fixups: DYLD_CHAINED_PTR_64_OFFSET",0,0

On main, the same chained-fixup fixture decodes no Objective-C classes.

Verification

  • npm run check:changed: 34 files and 294 tests passed.

  • npm run test:focused -- src/native/AppleMachoFixups.test.ts src/native/AppleDispatchMetadata.test.ts tests/boundary/cli/dispatcher.test.ts passed. The unit tests cover:

    • chained pointer bit layouts and import formats 1–3;
    • legacy bind opcodes;
    • property attribute parsing.
  • npm run docs:generate && npm run docs:check passed, and npm run check:fast passed.

  • Real lane: npm run verify:apple-dispatch passed all 10 variants on macOS arm64 (Apple clang/swiftc). The lane now builds each fixture three ways:

    • with -Wl,-no_fixup_chains (LC_DYLD_INFO);
    • with default chained fixups;
    • with -arch arm64e (ARM64E_USERLAND24, Objective-C only, Apple silicon only).

    For each build, the lane checks:

    • the external NSObject superclass;
    • the fixtureName property;
    • the external NSString (ReaDispatchAdditions) category and its implementation;
    • that Objective-C coverage is complete;
    • the pointer_fixups reason.
  • Manual spot checks on installed third-party apps:

    • KeepingYouAwake decodes 56 classes with all superclasses resolved and 7 external categories.
    • Raycast resolves 847 external superclasses.

No Hopper, Ghidra or IDA code is touched, so I didn't run any real provider verification.

🤖 Generated with Claude Code


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/953 **Author:** [@kaoru0822-kitauji](https://github.com/kaoru0822-kitauji) **Created:** 10/7/2026 **Status:** ✅ Merged **Merged:** 10/7/2026 **Merged by:** [@kaoru0822-kitauji](https://github.com/kaoru0822-kitauji) **Base:** `main` ← **Head:** `feat/macho-chained-fixups` --- ### 📝 Commits (1) - [`3e24a72`](https://github.com/morluto/rea/commit/3e24a72465fe6c628db1f836e93d74d7f4a92b80) feat(native): decode chained fixups, binds, ObjC categories and properties ### 📊 Changes **19 files changed** (+1464 additions, -239 deletions) <details> <summary>View changed files</summary> 📝 `docs/native-investigation.md` (+18 -5) 📝 `docs/product-catalog.json` (+2 -2) 📝 `docs/testing.md` (+18 -11) 📝 `docs/verification/managed-conformance-manifest.json` (+2 -2) 📝 `scripts/verify-apple-dispatch.mjs` (+181 -109) 📝 `skills/reverse-engineer-anything/SKILL.md` (+1 -1) 📝 `src/domain/nativeInvestigationGraph.test.ts` (+3 -0) 📝 `src/domain/objcSwiftMetadata.ts` (+18 -0) 📝 `src/generatedMcpToolCatalog.ts` (+1 -1) ➕ `src/native/AppleDispatchCoverage.ts` (+82 -0) 📝 `src/native/AppleDispatchMetadata.ts` (+30 -43) ➕ `src/native/AppleMachoFixups.test.ts` (+275 -0) ➕ `src/native/AppleMachoFixups.ts` (+414 -0) 📝 `src/native/AppleMachoSelection.ts` (+131 -38) ➕ `src/native/AppleObjcCategories.ts` (+99 -0) 📝 `src/native/AppleObjcDispatchFacets.ts` (+70 -25) ➕ `src/native/AppleObjcProperties.ts` (+103 -0) 📝 `src/native/AppleObjcProtocols.ts` (+8 -2) 📝 `tests/conformance/native/dispatch.m` (+8 -0) </details> ### 📄 Description ## Summary `inspect_native_dispatch_metadata` read Mach-O pointer fields as stored bytes. - Chained-fixup images, the linker default since Xcode 13, store fixup chains rather than addresses, so their class lists decoded nothing. - Superclasses defined in other images (`NSObject`, `NSViewController`, …) were always reported as `superclass_binding_unresolved`. This PR decodes pointer fixups before reading Objective-C metadata. It also adds the property and category records that the coverage facet previously marked unsupported. ### Fixups (`src/native/AppleMachoFixups.ts`) - **`LC_DYLD_CHAINED_FIXUPS`** - Supported pointer formats: `DYLD_CHAINED_PTR_64`, `64_OFFSET`, `ARM64E`, `ARM64E_USERLAND` and `ARM64E_USERLAND24`, covering plain and authenticated rebases and binds. - Supported import formats: `DYLD_CHAINED_IMPORT`, `_ADDEND` and `_ADDEND64`. - Compressed symbol tables and unknown pointer formats are rejected with the format named. The decoder does not guess for them. - **`LC_DYLD_INFO(_ONLY)`**: the bind, weak-bind and lazy-bind opcode streams are interpreted as an address-to-import map. Stream failures are reported in coverage and do not abort the decode. ### Objective-C - An external superclass is named from its `_OBJC_CLASS_$_Name` bind. `superclass_address` stays `null` for these because the class is not in the image. - `class_ro_t.baseProperties` is decoded into `objc_classes[].properties`, giving each property's name, type encoding, attributes and getter/setter. - `__objc_catlist` `category_t` records become the new `objc_categories` result. - Each record has the category's name, its class, and `class_source`, which is `external` (bound), `local` or `unresolved`. - It also lists instance and class method selectors, protocols, properties, location and Evidence. - Category methods appear in `objc_dispatch_implementations` with a `category` field. - Protocol-list counts are read as plain data, not through fixup decoding. ### Coverage - `objc_properties_categories` is now decoded and reports partial status with reasons. It was previously `unsupported`. - New `pointer_fixups` facet reports which encoding was used, for example `chained fixups: DYLD_CHAINED_PTR_64_OFFSET`, `LC_DYLD_INFO bind opcodes` or `no fixup load commands`, plus any failures. - The Swift generic/resilient facet is still `unsupported`. Swift `__swift5_fieldmd`/`__swift5_reflstr` and selector-ref projection are **not** in this PR. ## Contract changes (additive) - `objc_categories: ObjcCategory[]` is added to the dispatch metadata result. It is always present and empty when the image has no categories. - `objc_dispatch_implementations[].category?: string | null` is added. - There are two new or changed coverage facets: `objc_properties_categories` is now decoded, and `pointer_fixups` is new. CLI excerpt from `rea inspect-native-dispatch-metadata` on the conformance fixture (`tests/conformance/native/dispatch.m`, default chained-fixup link, arm64). Paths and digests are trimmed: ```yaml objc_classes[2]: - name: ReaDispatchFixture super_class: NSObject # from the _OBJC_CLASS_$_NSObject bind properties[1]: - name: fixtureName type_encoding: "@\"NSString\"" attributes[4]{name,value,...}: T,"@\"NSString\"" C,"" N,"" V,_fixtureName superclass_address: null decode: { status: decoded, reason: null } objc_categories[1]: - name: ReaDispatchAdditions class_name: NSString class_source: external instance_methods[1]: reaFixtureLength coverage: objc_properties_categories,complete,null,1,1 pointer_fixups,complete,"chained fixups: DYLD_CHAINED_PTR_64_OFFSET",0,0 ``` On `main`, the same chained-fixup fixture decodes no Objective-C classes. ## Verification - `npm run check:changed`: 34 files and 294 tests passed. - `npm run test:focused -- src/native/AppleMachoFixups.test.ts src/native/AppleDispatchMetadata.test.ts tests/boundary/cli/dispatcher.test.ts` passed. The unit tests cover: - chained pointer bit layouts and import formats 1–3; - legacy bind opcodes; - property attribute parsing. - `npm run docs:generate && npm run docs:check` passed, and `npm run check:fast` passed. - **Real lane**: `npm run verify:apple-dispatch` passed all 10 variants on macOS arm64 (Apple clang/swiftc). The lane now builds each fixture three ways: - with `-Wl,-no_fixup_chains` (`LC_DYLD_INFO`); - with default chained fixups; - with `-arch arm64e` (`ARM64E_USERLAND24`, Objective-C only, Apple silicon only). For each build, the lane checks: - the external `NSObject` superclass; - the `fixtureName` property; - the external `NSString (ReaDispatchAdditions)` category and its implementation; - that Objective-C coverage is complete; - the `pointer_fixups` reason. - Manual spot checks on installed third-party apps: - KeepingYouAwake decodes 56 classes with all superclasses resolved and 7 external categories. - Raycast resolves 847 external superclasses. No Hopper, Ghidra or IDA code is touched, so I didn't run any real provider verification. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-17 21:07:14 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#2531
No description provided.