[GH-ISSUE #5617] [Bug] get_objc_classes omits Hopper class labels from a stripped Objective-C binary #3405

Closed
opened 2026-10-31 00:40:00 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @coygeek on GitHub (Oct 20, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/5617

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Native analysis or decompilation

Observed behavior

With the Hopper provider, MCP get_objc_classes with {"pattern":"Hunt"} returns {"count":0,"classes":[]} for the stripped fixture below. On the same target, list_names {} exposes five matching class-object labels. The unstripped control returns five matching classes at exactly the same addresses.

A separate verifier independently reproduced this through the production stdio MCP entry point. All calls succeeded. The unfiltered stripped result contains only three imported class references and two GOT aliases; it omits all five fixture classes.

Reproduction

The independent verifier ran this stripped/control sequence once successfully:

  1. Save the fixture under Additional context as objc_fixture.m in a disposable directory. Run:

    clang -fobjc-arc -framework Foundation objc_fixture.m -o objc-unstripped
    cp objc-unstripped objc-stripped
    strip objc-stripped
    
  2. Start node scripts/rea.mjs mcp from the built source checkout below with a stdio MCP client and a working Hopper provider. The verifier used disposable HOME/XDG/TMPDIR roots and the explicit installed Hopper launcher.

  3. Call open_binary with {"path":"<absolute path to objc-stripped>","provider_id":"hopper"}. Call get_objc_classes with {"pattern":"Hunt"}, then {"pattern":""}; call list_names with {}.

  4. Call close_binary {}. Open objc-unstripped with the same provider and repeat both get_objc_classes calls. Close it before disconnecting. Both closes succeeded.

No new Hopper session was run during submission; the upstream check below is source inspection.

Expected behavior

Discover the five existing class-object labels, retaining them under the literal Hunt filter. src/contracts/enhancedToolContracts.ts:34-36, also confirmed in the live MCP catalog, advertises discovery and deduplication of Objective-C class labels with optional literal substring filtering. docs/tool-design.md:50-55 requires coverage/limitations and treats missing coverage as unknown rather than absence.

If this label convention is unsupported, explain the coverage limitation instead of returning an unexplained empty class result. This does not require universal runtime metadata decoding or definition/import classification.

Impact and scope

P2: callers receive an empty filtered class inventory despite the provider retaining five matching class labels. The demonstrated scope is this stripped arm64 Mach-O fixture with Hopper; other inputs/providers were not tested. Workarounds verified on the same target are inspecting list_names, or calling inspect_native_dispatch_metadata {} and selecting records with is_meta_class: false (five matching defined classes). No target corruption or security boundary crossing was observed.

REA version

6.7.0; source commit 074768901f3cdc8205b61773fb1faf6f6c7b3ca8. The latest published release checked during submission is rea-agents-6.7.0.

Runtime and platform

macOS arm64; Apple clang 21.0.0 (clang-2100.3.34.2); Hopper Disassembler 6.1.0-demo, build 42.6295552. NOT_ENOUGH_INFO: the verifier did not record the Node.js version or exact macOS version/build.

Target and artifact context

Locally compiled Objective-C/Foundation thin arm64 Mach-O executable, copied and stripped for comparison. Stripped SHA-256: a9581074fcddf1d0b56ff4279dc7ea4f98c87ebb5b4dcff5b1b3e74b63808165. Unstripped SHA-256: b030c4d0840d43bb5928732c77908a037d223c18cb096f68fec6ed0e3ba654e2. No binary or Hopper database is attached.

Interface

MCP

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Observed results:

Input pattern: "Hunt" count Unfiltered count
Stripped 0 5 (only imports/aliases)
Unstripped control 5 10

list_names {} on the stripped target returned all five omitted class-object labels:

0x100008590  __objc_class_HuntRoot_class
0x1000085e0  __objc_class_HuntSub_class
0x100008630  __objc_class_HuntWorker_class
0x100008680  __objc_class_HuntModel_class
0x1000086d0  __objc_class_HuntLegacy_class

The control returns _OBJC_CLASS_$_HuntRoot, _OBJC_CLASS_$_HuntSub, _OBJC_CLASS_$_HuntWorker, _OBJC_CLASS_$_HuntModel and _OBJC_CLASS_$_HuntLegacy at exactly those addresses. Independently run otool -ov objc-stripped reports the five class names; otool -l objc-stripped shows a __objc_classlist of size 0x28. nm -m objc-stripped retains only three undefined class references. The native dispatch metadata tool returns the five non-metaclass records at matching addresses.

The stripped class-discovery Evidence limitations mention workflow derivation and dependence on completed Hopper analysis, without explaining the excluded class-label convention.

Typed outcome or recovery expected

Return the five existing class-object labels under pattern: "Hunt", or explicitly report unsupported class-label coverage. A proposed restoration check is to repeat the same stripped/control calls, retain literal filtering and the five control classes, and avoid inventing classes from related method, ivar, property, data or metaclass labels. No fix or restored runtime was tested.

Analysis or suspected cause

Source-derived reasoning: src/server/registerEnhancedTools.ts:94-103 calls src/application/EnhancedTools.ts:287-289,673-690, which acquires and preserves inventory labels. src/domain/symbolAnalysis.ts:22-42 accepts the modern OBJC_CLASS_$_ marker and legacy l_OBJC_CLASS_ prefix; neither matches the observed __objc_class_Hunt…_class labels.

The submitter fetched origin/main at 5b676ba9f302228b44c28ab076525abed3e647c1. The class selector, inventory application code and cited contract/docs are unchanged from the reproduction baseline. Changes to the MCP registration concern profile/snapshot recording, and Hopper bridge changes concern bookmarks. No inspected change plausibly fixes this omission. This is source verification, not a runtime reproduction on current main.

Additional context

Prepared with AI assistance and independently reproduced by a separate verifier.

Related but different: #5607 concerns imported classes counted twice through GOT aliases. Deduplicating those aliases leaves these five defined-class labels excluded. Closed #156, merged PR #203 and closed PR #1313 concern false-positive non-class/bookkeeping labels rather than this omission. No assertion is made about the final unfiltered count after the separate alias issue is resolved.

Regression range: NOT_ENOUGH_INFO; no last-known-good version was established.

Exact compiled fixture
#import <Foundation/Foundation.h>

@protocol HuntProtocol <NSObject>
- (NSString *)huntName;
@end

@interface HuntRoot : NSObject
- (void)rootMethod;
@end
@implementation HuntRoot
- (void)rootMethod { NSLog(@"root"); }
@end

@interface HuntRoot (HuntCategory)
- (void)categoryMethod;
@end
@implementation HuntRoot (HuntCategory)
- (void)categoryMethod { NSLog(@"cat"); }
@end

@interface HuntSub : HuntRoot
@end
@implementation HuntSub
@end

@interface HuntWorker : NSObject <HuntProtocol>
@end
@implementation HuntWorker
- (NSString *)huntName { return @"worker"; }
@end

@interface HuntModel : NSObject
@property (nonatomic, copy) NSString *title;
@end
@implementation HuntModel
@end

@interface HuntLegacy : NSObject
+ (void)load;
@end
@implementation HuntLegacy
+ (void)load { }
@end

int main(void) {
  @autoreleasepool {
    HuntSub *s = [HuntSub new];
    [s rootMethod];
    [s categoryMethod];
    HuntWorker *w = [HuntWorker new];
    NSLog(@"%@ %@ %@", [w huntName], [NSArray array], [NSDictionary dictionary]);
    HuntModel *m = [HuntModel new];
    m.title = @"hello";
    NSLog(@"%@", m.title);
  }
  return 0;
}
Originally created by @coygeek on GitHub (Oct 20, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5617 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Native analysis or decompilation ### Observed behavior With the Hopper provider, MCP `get_objc_classes` with `{"pattern":"Hunt"}` returns `{"count":0,"classes":[]}` for the stripped fixture below. On the same target, `list_names {}` exposes five matching class-object labels. The unstripped control returns five matching classes at exactly the same addresses. A separate verifier independently reproduced this through the production stdio MCP entry point. All calls succeeded. The unfiltered stripped result contains only three imported class references and two GOT aliases; it omits all five fixture classes. ### Reproduction The independent verifier ran this stripped/control sequence once successfully: 1. Save the fixture under Additional context as `objc_fixture.m` in a disposable directory. Run: ```sh clang -fobjc-arc -framework Foundation objc_fixture.m -o objc-unstripped cp objc-unstripped objc-stripped strip objc-stripped ``` 2. Start `node scripts/rea.mjs mcp` from the built source checkout below with a stdio MCP client and a working Hopper provider. The verifier used disposable HOME/XDG/TMPDIR roots and the explicit installed Hopper launcher. 3. Call `open_binary` with `{"path":"<absolute path to objc-stripped>","provider_id":"hopper"}`. Call `get_objc_classes` with `{"pattern":"Hunt"}`, then `{"pattern":""}`; call `list_names` with `{}`. 4. Call `close_binary {}`. Open `objc-unstripped` with the same provider and repeat both `get_objc_classes` calls. Close it before disconnecting. Both closes succeeded. No new Hopper session was run during submission; the upstream check below is source inspection. ### Expected behavior Discover the five existing class-object labels, retaining them under the literal `Hunt` filter. `src/contracts/enhancedToolContracts.ts:34-36`, also confirmed in the live MCP catalog, advertises discovery and deduplication of Objective-C class labels with optional literal substring filtering. `docs/tool-design.md:50-55` requires coverage/limitations and treats missing coverage as unknown rather than absence. If this label convention is unsupported, explain the coverage limitation instead of returning an unexplained empty class result. This does not require universal runtime metadata decoding or definition/import classification. ### Impact and scope P2: callers receive an empty filtered class inventory despite the provider retaining five matching class labels. The demonstrated scope is this stripped arm64 Mach-O fixture with Hopper; other inputs/providers were not tested. Workarounds verified on the same target are inspecting `list_names`, or calling `inspect_native_dispatch_metadata {}` and selecting records with `is_meta_class: false` (five matching defined classes). No target corruption or security boundary crossing was observed. ### REA version 6.7.0; source commit `074768901f3cdc8205b61773fb1faf6f6c7b3ca8`. The latest published release checked during submission is `rea-agents-6.7.0`. ### Runtime and platform macOS arm64; Apple clang 21.0.0 (clang-2100.3.34.2); Hopper Disassembler 6.1.0-demo, build 42.6295552. NOT_ENOUGH_INFO: the verifier did not record the Node.js version or exact macOS version/build. ### Target and artifact context Locally compiled Objective-C/Foundation thin arm64 Mach-O executable, copied and stripped for comparison. Stripped SHA-256: `a9581074fcddf1d0b56ff4279dc7ea4f98c87ebb5b4dcff5b1b3e74b63808165`. Unstripped SHA-256: `b030c4d0840d43bb5928732c77908a037d223c18cb096f68fec6ed0e3ba654e2`. No binary or Hopper database is attached. ### Interface MCP ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence Observed results: | Input | `pattern: "Hunt"` count | Unfiltered count | | --- | --- | --- | | Stripped | 0 | 5 (only imports/aliases) | | Unstripped control | 5 | 10 | `list_names {}` on the stripped target returned all five omitted class-object labels: ```text 0x100008590 __objc_class_HuntRoot_class 0x1000085e0 __objc_class_HuntSub_class 0x100008630 __objc_class_HuntWorker_class 0x100008680 __objc_class_HuntModel_class 0x1000086d0 __objc_class_HuntLegacy_class ``` The control returns `_OBJC_CLASS_$_HuntRoot`, `_OBJC_CLASS_$_HuntSub`, `_OBJC_CLASS_$_HuntWorker`, `_OBJC_CLASS_$_HuntModel` and `_OBJC_CLASS_$_HuntLegacy` at exactly those addresses. Independently run `otool -ov objc-stripped` reports the five class names; `otool -l objc-stripped` shows a `__objc_classlist` of size `0x28`. `nm -m objc-stripped` retains only three undefined class references. The native dispatch metadata tool returns the five non-metaclass records at matching addresses. The stripped class-discovery Evidence limitations mention workflow derivation and dependence on completed Hopper analysis, without explaining the excluded class-label convention. ### Typed outcome or recovery expected Return the five existing class-object labels under `pattern: "Hunt"`, or explicitly report unsupported class-label coverage. A proposed restoration check is to repeat the same stripped/control calls, retain literal filtering and the five control classes, and avoid inventing classes from related method, ivar, property, data or metaclass labels. No fix or restored runtime was tested. ### Analysis or suspected cause Source-derived reasoning: `src/server/registerEnhancedTools.ts:94-103` calls `src/application/EnhancedTools.ts:287-289,673-690`, which acquires and preserves inventory labels. `src/domain/symbolAnalysis.ts:22-42` accepts the modern `OBJC_CLASS_$_` marker and legacy `l_OBJC_CLASS_` prefix; neither matches the observed `__objc_class_Hunt…_class` labels. The submitter fetched `origin/main` at `5b676ba9f302228b44c28ab076525abed3e647c1`. The class selector, inventory application code and cited contract/docs are unchanged from the reproduction baseline. Changes to the MCP registration concern profile/snapshot recording, and Hopper bridge changes concern bookmarks. No inspected change plausibly fixes this omission. This is source verification, not a runtime reproduction on current main. ### Additional context Prepared with AI assistance and independently reproduced by a separate verifier. Related but different: #5607 concerns imported classes counted twice through GOT aliases. Deduplicating those aliases leaves these five defined-class labels excluded. Closed #156, merged PR #203 and closed PR #1313 concern false-positive non-class/bookkeeping labels rather than this omission. No assertion is made about the final unfiltered count after the separate alias issue is resolved. Regression range: NOT_ENOUGH_INFO; no last-known-good version was established. <details> <summary>Exact compiled fixture</summary> ```objc #import <Foundation/Foundation.h> @protocol HuntProtocol <NSObject> - (NSString *)huntName; @end @interface HuntRoot : NSObject - (void)rootMethod; @end @implementation HuntRoot - (void)rootMethod { NSLog(@"root"); } @end @interface HuntRoot (HuntCategory) - (void)categoryMethod; @end @implementation HuntRoot (HuntCategory) - (void)categoryMethod { NSLog(@"cat"); } @end @interface HuntSub : HuntRoot @end @implementation HuntSub @end @interface HuntWorker : NSObject <HuntProtocol> @end @implementation HuntWorker - (NSString *)huntName { return @"worker"; } @end @interface HuntModel : NSObject @property (nonatomic, copy) NSString *title; @end @implementation HuntModel @end @interface HuntLegacy : NSObject + (void)load; @end @implementation HuntLegacy + (void)load { } @end int main(void) { @autoreleasepool { HuntSub *s = [HuntSub new]; [s rootMethod]; [s categoryMethod]; HuntWorker *w = [HuntWorker new]; NSLog(@"%@ %@ %@", [w huntName], [NSArray array], [NSDictionary dictionary]); HuntModel *m = [HuntModel new]; m.title = @"hello"; NSLog(@"%@", m.title); } return 0; } ``` </details>
Author
Owner

@matbrgz commented on GitHub (Oct 20, 2026):

Cross-referencing with canonical issue #5576 ([Bug] analyze_swift_types counts Objective-C labels containing _Tt as Swift observations) and siblings #5580 and #5607. Both #5607 and #5617 uncover class enumeration and label resolution defects in get_objc_classes with the Hopper provider (duplicate class counts via GOT aliases vs omitted class labels in stripped binaries). Cataloged under Cluster BH in consolidation tracker #4330.

<!-- gh-comment-id:6296082192 --> @matbrgz commented on GitHub (Oct 20, 2026): Cross-referencing with canonical issue #5576 (`[Bug] analyze_swift_types counts Objective-C labels containing _Tt as Swift observations`) and siblings #5580 and #5607. Both #5607 and #5617 uncover class enumeration and label resolution defects in `get_objc_classes` with the Hopper provider (duplicate class counts via GOT aliases vs omitted class labels in stripped binaries). Cataloged under Cluster BH in consolidation tracker #4330.
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#3405
No description provided.