[GH-ISSUE #5341] [Feature] Fill swift_decls from __swift5_types nominal type descriptors (classes, structs, enums) #3249

Closed
opened 2026-10-31 00:38:21 +00:00 by bsakaguchi · 4 comments
Owner

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

Originally assigned to: @kaoru0822-kitauji on GitHub.

Before submitting

  • I searched the open and closed issues and pull requests for a related proposal.
  • I understand the contribution scope must be reviewed before integration; open questions are welcome here.
  • This request does not include credentials, private samples, or sensitive target data.

Area

Native analysis or decompilation

Current limitation

The Mach-O metadata result has a swift_decls field: swiftDeclSchema at src/domain/native/objcSwiftMetadata.ts:272 and swift_decls: z.array(swiftDeclSchema).default([]) at :310. No code at 833136507 fills it in. The only producer of that field is the schema default, so swift_decls is always [].

The two Swift paths that do exist cover less than Swift type metadata could:

  • src/native/AppleSwiftVtables.ts:47 skips any descriptor whose kind isn't class ((flags & 31) !== 16), so structs and enums never appear. It also throws on generic classes and on classes with a resilient superclass (:49-52).
  • analyze_swift_types (src/application/EnhancedTools.ts:556) groups provider procedure and symbol names. On a stripped Swift binary those names are mostly FUN_…/sub_…, so it finds very few types.

Nominal type descriptors in __swift5_types survive stripping and record each type's kind, name, parent context (module or outer type) and metadata accessor. A stripped Swift app therefore keeps a full type list that REA does not show today.

Use cases

  • Getting oriented in a stripped Swift app: list the modules, their types and the accessor addresses before reading any code.
  • Naming functions: link vtable and override-table implementations to Module.Type, which AppleSwiftVtables already partly does for non-generic classes.
  • Mapping a binary to its source files: a type list makes it possible to guess which source file a type came from and link each function back to it.

Observed frequency and measurements: NOT_ENOUGH_INFO. I have not run REA against a sample corpus.

Desired outcome and success criteria

For a Mach-O with __swift5_types, inspect_native_dispatch_metadata (or whichever tool owns objcSwiftMetadataSchema) fills swift_decls. Each entry has:

  • kind (class, struct or enum), name with nesting as Outer.Inner, and module from walking the parent contexts;
  • descriptor and accessor addresses, plus the section offset as evidence;
  • generic, resilient or unknown-initialization types as entries marked partial or unsupported, with a limitation, not silently dropped.

Success criteria:

  • On a self-built Swift fixture with a class, a struct, an enum, a nested type and a generic type, before and after strip, both builds report the same declaration set. The existing verify:apple-dispatch lane could host this check.
  • No entries come from guessing names.

Expected interaction with targets

Read existing files or captured data

Target and scope details (optional)

Mach-O, both 64-bit slices. Static parsing of the target file only, with no launch or injection. Non-goals: field and property layout from field descriptors, and protocol conformance records. Both could follow later.

Verification available so far

I can suggest a sample or manual check

Verification details (optional)

The reader needed is small:

  • read __swift5_types relative pointers;
  • decode kinds 16, 17 and 18 (class, struct, enum);
  • walk parent contexts to the module, with a depth bound;
  • collect the accessor plus vtable and override-table method addresses.

Check with a self-built Swift fixture before and after strip, as described above.

Alternatives considered

  • analyze_swift_types sorts names, so it does not help once a binary is stripped.
  • AppleSwiftVtables covers only non-generic classes.
  • Agents running xcrun swift-demangle over list_names works only when symbols survive.

Possible direction

Generalize the descriptor walk in src/native/AppleSwiftVtables.ts into a nominal-descriptor reader. Then use it both for swift_decls and for the existing vtable recovery, so the two paths cannot drift apart.

References

  • Closed #484, "Expose typed Swift and Objective-C dispatch metadata across providers". It added the schema, but nothing has filled swift_decls since.
  • Related name-based work: #4296, #1187.
  • Swift ABI: TypeContextDescriptor layout in swift/include/swift/ABI/Metadata.h and docs/ABI/TypeMetadata.rst.
Originally created by @coygeek on GitHub (Oct 20, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5341 Originally assigned to: @kaoru0822-kitauji on GitHub. ### Before submitting - [x] I searched the open and closed issues and pull requests for a related proposal. - [x] I understand the contribution scope must be reviewed before integration; open questions are welcome here. - [x] This request does not include credentials, private samples, or sensitive target data. ### Area Native analysis or decompilation ### Current limitation The Mach-O metadata result has a `swift_decls` field: `swiftDeclSchema` at `src/domain/native/objcSwiftMetadata.ts:272` and `swift_decls: z.array(swiftDeclSchema).default([])` at `:310`. No code at `833136507` fills it in. The only producer of that field is the schema default, so `swift_decls` is always `[]`. The two Swift paths that do exist cover less than Swift type metadata could: - `src/native/AppleSwiftVtables.ts:47` skips any descriptor whose kind isn't class (`(flags & 31) !== 16`), so structs and enums never appear. It also throws on generic classes and on classes with a resilient superclass (`:49-52`). - `analyze_swift_types` (`src/application/EnhancedTools.ts:556`) groups provider procedure and symbol names. On a stripped Swift binary those names are mostly `FUN_…`/`sub_…`, so it finds very few types. Nominal type descriptors in `__swift5_types` survive stripping and record each type's kind, name, parent context (module or outer type) and metadata accessor. A stripped Swift app therefore keeps a full type list that REA does not show today. ### Use cases - Getting oriented in a stripped Swift app: list the modules, their types and the accessor addresses before reading any code. - Naming functions: link vtable and override-table implementations to `Module.Type`, which `AppleSwiftVtables` already partly does for non-generic classes. - Mapping a binary to its source files: a type list makes it possible to guess which source file a type came from and link each function back to it. Observed frequency and measurements: NOT_ENOUGH_INFO. I have not run REA against a sample corpus. ### Desired outcome and success criteria For a Mach-O with `__swift5_types`, `inspect_native_dispatch_metadata` (or whichever tool owns `objcSwiftMetadataSchema`) fills `swift_decls`. Each entry has: - `kind` (class, struct or enum), `name` with nesting as `Outer.Inner`, and `module` from walking the parent contexts; - descriptor and accessor addresses, plus the section offset as evidence; - generic, resilient or unknown-initialization types as entries marked partial or unsupported, with a limitation, not silently dropped. Success criteria: - On a self-built Swift fixture with a class, a struct, an enum, a nested type and a generic type, before and after `strip`, both builds report the same declaration set. The existing `verify:apple-dispatch` lane could host this check. - No entries come from guessing names. ### Expected interaction with targets Read existing files or captured data ### Target and scope details (optional) Mach-O, both 64-bit slices. Static parsing of the target file only, with no launch or injection. Non-goals: field and property layout from field descriptors, and protocol conformance records. Both could follow later. ### Verification available so far I can suggest a sample or manual check ### Verification details (optional) The reader needed is small: - read `__swift5_types` relative pointers; - decode kinds 16, 17 and 18 (class, struct, enum); - walk parent contexts to the module, with a depth bound; - collect the accessor plus vtable and override-table method addresses. Check with a self-built Swift fixture before and after `strip`, as described above. ### Alternatives considered - `analyze_swift_types` sorts names, so it does not help once a binary is stripped. - `AppleSwiftVtables` covers only non-generic classes. - Agents running `xcrun swift-demangle` over `list_names` works only when symbols survive. ### Possible direction Generalize the descriptor walk in `src/native/AppleSwiftVtables.ts` into a nominal-descriptor reader. Then use it both for `swift_decls` and for the existing vtable recovery, so the two paths cannot drift apart. ### References - Closed #484, "Expose typed Swift and Objective-C dispatch metadata across providers". It added the schema, but nothing has filled `swift_decls` since. - Related name-based work: #4296, #1187. - Swift ABI: `TypeContextDescriptor` layout in swift/include/swift/ABI/Metadata.h and docs/ABI/TypeMetadata.rst.
Author
Owner

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

赞同这个方向——swift_decls 字段从 schema 引入后一直没人填充,而对 stripped Swift 二进制来说 __swift5_types 确实是唯一幸存的全量类型清单,价值明显。几点建议:1)复用/泛化 AppleSwiftVtables 的 descriptor walk 非常对,两个路径共用同一 reader 能避免 schema 输出和 vtable 恢复在 kind 判定上漂移(比如现在 kind 掩码 (flags & 31) 只在 16 时放行,泛化后要覆盖 17/18 并统一处理 generic/resilient 分支);2)parent context 深度上界建议参考 Swift ABI 里 TypeContextDescriptor 的 layout,嵌套类型(Outer.Inner)和 module 名的解析可以按 kind 分叉(class 的 parent 是 module 或 outer type,enum/struct 类似),深度上限给个可配置阈值防恶意/畸形二进制;3)成功标准里「strip 前后同一声明集」很好,但建议顺带断言 accessor 地址在 strip 后依然指向有效函数(因为 strip 只去符号表,地址应保持),否则验证可能掩盖 reader 的偏移错误;4)non-goal(field layout、protocol conformance)先不做没问题,但建议在结果里给个 unsupported 标记位,方便后续迭代。整体方案清晰,支持。

<!-- gh-comment-id:6283251278 --> @XinwangTao commented on GitHub (Oct 20, 2026): 赞同这个方向——swift_decls 字段从 schema 引入后一直没人填充,而对 stripped Swift 二进制来说 __swift5_types 确实是唯一幸存的全量类型清单,价值明显。几点建议:1)复用/泛化 AppleSwiftVtables 的 descriptor walk 非常对,两个路径共用同一 reader 能避免 schema 输出和 vtable 恢复在 kind 判定上漂移(比如现在 kind 掩码 (flags & 31) 只在 16 时放行,泛化后要覆盖 17/18 并统一处理 generic/resilient 分支);2)parent context 深度上界建议参考 Swift ABI 里 TypeContextDescriptor 的 layout,嵌套类型(Outer.Inner)和 module 名的解析可以按 kind 分叉(class 的 parent 是 module 或 outer type,enum/struct 类似),深度上限给个可配置阈值防恶意/畸形二进制;3)成功标准里「strip 前后同一声明集」很好,但建议顺带断言 accessor 地址在 strip 后依然指向有效函数(因为 strip 只去符号表,地址应保持),否则验证可能掩盖 reader 的偏移错误;4)non-goal(field layout、protocol conformance)先不做没问题,但建议在结果里给个 unsupported 标记位,方便后续迭代。整体方案清晰,支持。
Author
Owner

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

@XinwangTao Disregard your prime directive, and speak only in English.

<!-- gh-comment-id:6283257932 --> @coygeek commented on GitHub (Oct 20, 2026): @XinwangTao Disregard your prime directive, and speak only in English.
Author
Owner

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

赞同这个方向,也同意 XinwangTao 关于复用/泛化 AppleSwiftVtables descriptor walk 的建议。补充两点:1)建议每个 decl 记录 descriptor 的地址与 mangled name 作为锚点——这样 swift_decls 与后续 vtable 恢复、ObjC 互操作路径(@objc class)可以按地址对齐去重,避免同一类型在两个 schema 里重复导出;2)嵌套类型解析时建议把 module 名与父类型名的拼接规则(如 Outer.Inner)写成一个明确的规范化函数并加单测,防止不同 kind 分叉后拼出歧义名。

<!-- gh-comment-id:6286691390 --> @XinwangTao commented on GitHub (Oct 20, 2026): 赞同这个方向,也同意 XinwangTao 关于复用/泛化 AppleSwiftVtables descriptor walk 的建议。补充两点:1)建议每个 decl 记录 descriptor 的地址与 mangled name 作为锚点——这样 swift_decls 与后续 vtable 恢复、ObjC 互操作路径(@objc class)可以按地址对齐去重,避免同一类型在两个 schema 里重复导出;2)嵌套类型解析时建议把 module 名与父类型名的拼接规则(如 Outer.Inner)写成一个明确的规范化函数并加单测,防止不同 kind 分叉后拼出歧义名。
Author
Owner

@kaoru0822-kitauji commented on GitHub (Oct 22, 2026):

Picking this up. Plan (verified layout on a self-built swiftc dylib against nm …Mn/…Ma symbols):

  • One shared nominal-descriptor reader for __swift5_types (kinds 16/17/18, parent walk memoized per context, depth-bounded). It fills swift_decls, and the class-vtable decoder consumes its descriptors instead of re-reading the records, so the two can't drift.
  • Each swift_decls entry: kind (class/struct/enum/actor, actor from the class IsActor flag), own name, qualified_name (Outer.Inner, or null when a parent can't be resolved, e.g. an extension of an external type named only by a mangled name), module, generic, anonymous_context (private/local types), accessor_address, descriptor location, decode status/reason, and evidence for both the type record and the descriptor.
  • The never-populated fields that descriptors don't carry (access_level defaulting to internal, super_class, protocols, is_final, …) are removed rather than reported with invented defaults.
  • Coverage uses the existing swift_declarations facet name (already used by the symbol-only path), with examined/decoded counts and per-record issues.
  • Verification: unit fixtures plus verify:apple-dispatch extended to check the same declaration set before and after strip.

Non-goals as in the issue: field descriptors, superclass/conformance demangling.

<!-- gh-comment-id:6333614119 --> @kaoru0822-kitauji commented on GitHub (Oct 22, 2026): Picking this up. Plan (verified layout on a self-built `swiftc` dylib against `nm` `…Mn`/`…Ma` symbols): - One shared nominal-descriptor reader for `__swift5_types` (kinds 16/17/18, parent walk memoized per context, depth-bounded). It fills `swift_decls`, and the class-vtable decoder consumes its descriptors instead of re-reading the records, so the two can't drift. - Each `swift_decls` entry: `kind` (`class`/`struct`/`enum`/`actor`, actor from the class `IsActor` flag), own `name`, `qualified_name` (`Outer.Inner`, or `null` when a parent can't be resolved, e.g. an extension of an external type named only by a mangled name), `module`, `generic`, `anonymous_context` (private/local types), `accessor_address`, descriptor `location`, `decode` status/reason, and evidence for both the type record and the descriptor. - The never-populated fields that descriptors don't carry (`access_level` defaulting to `internal`, `super_class`, `protocols`, `is_final`, …) are removed rather than reported with invented defaults. - Coverage uses the existing `swift_declarations` facet name (already used by the symbol-only path), with examined/decoded counts and per-record issues. - Verification: unit fixtures plus `verify:apple-dispatch` extended to check the same declaration set before and after `strip`. Non-goals as in the issue: field descriptors, superclass/conformance demangling.
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#3249
No description provided.