[PR #2364] [MERGED] fix(native): parse renamed dyld_info re-exports #5189

Closed
opened 2026-10-31 01:04:40 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/2364
Author: @Chaoqi31
Created: 10/12/2026
Status: ✅ Merged
Merged: 10/13/2026
Merged by: @morluto

Base: main ← Head: fix/macho-renamed-reexport


📝 Commits (5)

  • a04874f fix(native): parse renamed dyld_info re-exports
  • 1574be4 fix(native): split dyld re-export suffix without nested quantifiers
  • 3c3eb57 fix: reconcile captured main and owned findings for PR #2364
  • 1ff17d7 test(native): cover nested re-export library names
  • 89bfda5 fix(native): retain reexports missing from nm inventory

📊 Changes

5 files changed (+196 additions, -4 deletions)

View changed files

📝 src/native/NativeMachoInspection.ts (+5 -0)
📝 src/native/parsers/dyldInfo.ts (+81 -4)
📝 tests/boundary/providers/native/nativeInventory.golden.test.ts (+92 -0)
📝 tests/fixtures/native-macos/dyld-inventory/README.md (+13 -0)
➕ tests/fixtures/native-macos/dyld-inventory/renamed-reexports.txt (+5 -0)

📄 Description

Summary

inspect_macho misreported a dylib that re-exports a symbol under a new name. It listed an export named _foo_renamed (_foo from libfoo) that does not exist, with no source library, and kept nm's _foo_renamed as a second, unattributed export. The export now appears once, as _foo_renamed re-exported from libfoo, and the exports coverage states that the source library's own symbol name is not reported.

No issue is linked. I found this while comparing inspect_macho exports with xcrun dyld_info -exports on synthetic dylibs.

Problem and expected behavior

Smallest trigger: a dylib linked against libfoo.dylib with -Wl,-alias,_foo,_foo_renamed. xcrun dyld_info -exports prints the renamed re-export with its source symbol:

libre.dylib [arm64]:
    -exports:
        offset      symbol
        [re-export] _foo_renamed (_foo from libfoo)
        0x00000308  _own

parseDyldSymbols in src/native/parsers/dyldInfo.ts only accepted (from libfoo), so the whole remainder became the name and the source was null. NativeMachoInspection merges dyld rows with nm rows by name, so nm's _foo_renamed matched nothing and stayed as a second row with reexport: null.

Expected: one row per exported name, flagged as a re-export with its source library, the same as an ordinary [re-export] _sym (from lib) row.

Change and scope

The re-export row pattern accepts an optional source symbol before from. It tries the plain (from lib) form first, so a library whose install name contains from keeps its whole name, and a source symbol may itself contain parentheses, such as operator(). The row is named by its exported name and replaces the nm row like any other re-export.

The export item schema has no field for the source symbol (_foo), and adding one is a contract change. This PR keeps it out of the items and adds an exports.limitations entry whenever a renamed re-export is present, so the omission is visible. Ordinary re-export rows and import rows parse as before.

Contribution scope: not applicable (ordinary fix within existing boundaries).

Contract and boundary impact

  • Semantic owner and earliest changed stage: parseDyldSymbols (src/native/parsers/dyldInfo.ts), which now also returns the source symbol, and NativeMachoInspection, which states the limitation.
  • Contribution scope fit and linked maintainer decision: ordinary fix; not applicable.
  • CLI and MCP/tool-catalog contract: no schema change. exports.items changes only for renamed re-exports, and exports.limitations gains one entry when they are present.
  • Provider, bridge, target-format, or platform compatibility: macOS native provider only. Other dyld_info row shapes keep their existing golden coverage.
  • Evidence, artifact, provenance, or reconstruction contract: removes a fabricated export name. The source symbol name is not represented, and the limitation says so.
  • Target selection, authority, upstream behavior, and cleanup details (if applicable): not applicable.
  • Generated metadata (docs/public/product-catalog.json), package, or installation impact: none.

Expected effects (check known effects; use documentation-only on its own).
Unchecked items do not establish absence; leave uncertainties visible.

  • Read existing files or captured data.
  • Launch or attach to a process.
  • Inject instrumentation or run scripts/code.
  • Change process memory, registers, files, or configuration.
  • Contact network services.
  • Recover protected code or work around protections.
  • Documentation or usability only; no target interaction.
  • Not sure yet; investigation or maintainer guidance needed.

Evidence and regression coverage

  • Tests added or updated: tests/boundary/providers/native/nativeInventory.golden.test.ts adds two tests. "reports a renamed re-export once under its exported name" runs inspect_macho through FixtureRunner with a new golden capture, tests/fixtures/native-macos/dyld-inventory/renamed-reexports.txt, and checks the items and limitations. The fixture README records the commands that produce it. "splits re-export rows at the source library" parses rows captured from synthetic dylibs whose source symbol is operator() and whose library install name is lib from foo.

  • Base reproduction or other evidence: with dyldInfo.ts and NativeMachoInspection.ts restored from main (fd958808), both new tests fail and the other 14 tests in the file pass.

  • User-visible CLI/MCP output (if applicable): rea inspect-macho libre.dylib --format json on the dylib above, normalized_result.exports.

    Before (main):

    {"name": "_foo_renamed", "address": null, "weak": null, "reexport": null, "source": "nm"}
    {"name": "_foo_renamed (_foo from libfoo)", "address": null, "weak": null, "reexport": true, "source": null}
    {"name": "_own", "address": "0x308", "weak": null, "reexport": false, "source": null}
    

    After:

    {"name": "_foo_renamed", "address": null, "weak": null, "reexport": true, "source": "libfoo"}
    {"name": "_own", "address": "0x308", "weak": null, "reexport": false, "source": null}
    limitations: ["Merged nm/dyld_info results may be incomplete for stripped binaries.", "Renamed re-exports are listed under their exported name; the source library's symbol name is not reported."]
    
  • Remaining proof gaps: captured on arm64 with the Xcode toolchain on this Mac; I did not capture x86_64 output. dyld_info text cannot distinguish an exported name that itself contains (… from …); such a name would be split inside the name.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

  • On the branch rebased onto main fd958808, with dependencies installed by npm ci: npm run check:fast passes.
  • npm run test:focused over the 24 test files in tests/boundary/providers/native/native*.test.ts and src/native/. 279 pass and 3 fail. The 3 failures are the real-hdiutil cases in nativeDmgArtifactReader.test.ts; they fail the same way on main fd958808 on this Mac, and this change does not touch DMG code.
  • The before/after CLI run above, with npm run build:cached on each checkout.

Compatibility, safety, and release

  • Breaking changes or migration steps: none.
  • Real Hopper/Ghidra, browser, or OS coverage: real xcrun dyld_info and nm on macOS (arm64) for synthetic dylibs.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: none; parser change only.

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🔄 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/2364 **Author:** [@Chaoqi31](https://github.com/Chaoqi31) **Created:** 10/12/2026 **Status:** ✅ Merged **Merged:** 10/13/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `fix/macho-renamed-reexport` --- ### 📝 Commits (5) - [`a04874f`](https://github.com/morluto/rea/commit/a04874ffe4eb1385a799e11757b83efd42b60130) fix(native): parse renamed dyld_info re-exports - [`1574be4`](https://github.com/morluto/rea/commit/1574be4876f0ee1a85afb5d5fc27c9b81ffb4ac3) fix(native): split dyld re-export suffix without nested quantifiers - [`3c3eb57`](https://github.com/morluto/rea/commit/3c3eb579db02d191cc481ead4ab311e3280af3f9) fix: reconcile captured main and owned findings for PR #2364 - [`1ff17d7`](https://github.com/morluto/rea/commit/1ff17d73fe4ab2c7311c1e667de19e32764211ad) test(native): cover nested re-export library names - [`89bfda5`](https://github.com/morluto/rea/commit/89bfda597426bdfcc737b7e515e771ed5f62f4d6) fix(native): retain reexports missing from nm inventory ### 📊 Changes **5 files changed** (+196 additions, -4 deletions) <details> <summary>View changed files</summary> 📝 `src/native/NativeMachoInspection.ts` (+5 -0) 📝 `src/native/parsers/dyldInfo.ts` (+81 -4) 📝 `tests/boundary/providers/native/nativeInventory.golden.test.ts` (+92 -0) 📝 `tests/fixtures/native-macos/dyld-inventory/README.md` (+13 -0) ➕ `tests/fixtures/native-macos/dyld-inventory/renamed-reexports.txt` (+5 -0) </details> ### 📄 Description ## Summary `inspect_macho` misreported a dylib that re-exports a symbol under a new name. It listed an export named `_foo_renamed (_foo from libfoo)` that does not exist, with no source library, and kept `nm`'s `_foo_renamed` as a second, unattributed export. The export now appears once, as `_foo_renamed` re-exported from `libfoo`, and the exports coverage states that the source library's own symbol name is not reported. No issue is linked. I found this while comparing `inspect_macho` exports with `xcrun dyld_info -exports` on synthetic dylibs. ## Problem and expected behavior Smallest trigger: a dylib linked against `libfoo.dylib` with `-Wl,-alias,_foo,_foo_renamed`. `xcrun dyld_info -exports` prints the renamed re-export with its source symbol: ``` libre.dylib [arm64]: -exports: offset symbol [re-export] _foo_renamed (_foo from libfoo) 0x00000308 _own ``` `parseDyldSymbols` in `src/native/parsers/dyldInfo.ts` only accepted `(from libfoo)`, so the whole remainder became the name and the source was null. `NativeMachoInspection` merges dyld rows with `nm` rows by name, so `nm`'s `_foo_renamed` matched nothing and stayed as a second row with `reexport: null`. Expected: one row per exported name, flagged as a re-export with its source library, the same as an ordinary `[re-export] _sym (from lib)` row. ## Change and scope The re-export row pattern accepts an optional source symbol before `from`. It tries the plain `(from lib)` form first, so a library whose install name contains ` from ` keeps its whole name, and a source symbol may itself contain parentheses, such as `operator()`. The row is named by its exported name and replaces the `nm` row like any other re-export. The export item schema has no field for the source symbol (`_foo`), and adding one is a contract change. This PR keeps it out of the items and adds an `exports.limitations` entry whenever a renamed re-export is present, so the omission is visible. Ordinary re-export rows and import rows parse as before. Contribution scope: not applicable (ordinary fix within existing boundaries). ## Contract and boundary impact - Semantic owner and earliest changed stage: `parseDyldSymbols` (`src/native/parsers/dyldInfo.ts`), which now also returns the source symbol, and `NativeMachoInspection`, which states the limitation. - Contribution scope fit and linked maintainer decision: ordinary fix; not applicable. - CLI and MCP/tool-catalog contract: no schema change. `exports.items` changes only for renamed re-exports, and `exports.limitations` gains one entry when they are present. - Provider, bridge, target-format, or platform compatibility: macOS native provider only. Other `dyld_info` row shapes keep their existing golden coverage. - Evidence, artifact, provenance, or reconstruction contract: removes a fabricated export name. The source symbol name is not represented, and the limitation says so. - Target selection, authority, upstream behavior, and cleanup details (if applicable): not applicable. - Generated metadata (`docs/public/product-catalog.json`), package, or installation impact: none. Expected effects (check known effects; use documentation-only on its own). Unchecked items do not establish absence; leave uncertainties visible. - [x] Read existing files or captured data. - [ ] Launch or attach to a process. - [ ] Inject instrumentation or run scripts/code. - [ ] Change process memory, registers, files, or configuration. - [ ] Contact network services. - [ ] Recover protected code or work around protections. - [ ] Documentation or usability only; no target interaction. - [ ] Not sure yet; investigation or maintainer guidance needed. ## Evidence and regression coverage - Tests added or updated: `tests/boundary/providers/native/nativeInventory.golden.test.ts` adds two tests. "reports a renamed re-export once under its exported name" runs `inspect_macho` through `FixtureRunner` with a new golden capture, `tests/fixtures/native-macos/dyld-inventory/renamed-reexports.txt`, and checks the items and limitations. The fixture README records the commands that produce it. "splits re-export rows at the source library" parses rows captured from synthetic dylibs whose source symbol is `operator()` and whose library install name is `lib from foo`. - Base reproduction or other evidence: with `dyldInfo.ts` and `NativeMachoInspection.ts` restored from `main` (fd958808), both new tests fail and the other 14 tests in the file pass. - User-visible CLI/MCP output (if applicable): `rea inspect-macho libre.dylib --format json` on the dylib above, `normalized_result.exports`. Before (`main`): ``` {"name": "_foo_renamed", "address": null, "weak": null, "reexport": null, "source": "nm"} {"name": "_foo_renamed (_foo from libfoo)", "address": null, "weak": null, "reexport": true, "source": null} {"name": "_own", "address": "0x308", "weak": null, "reexport": false, "source": null} ``` After: ``` {"name": "_foo_renamed", "address": null, "weak": null, "reexport": true, "source": "libfoo"} {"name": "_own", "address": "0x308", "weak": null, "reexport": false, "source": null} limitations: ["Merged nm/dyld_info results may be incomplete for stripped binaries.", "Renamed re-exports are listed under their exported name; the source library's symbol name is not reported."] ``` - Remaining proof gaps: captured on arm64 with the Xcode toolchain on this Mac; I did not capture x86_64 output. `dyld_info` text cannot distinguish an exported name that itself contains ` (… from …)`; such a name would be split inside the name. For evidence-bearing changes: - [x] Observed, derived, and inferred claims remain distinguishable. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Validation performed - On the branch rebased onto `main` fd958808, with dependencies installed by `npm ci`: `npm run check:fast` passes. - `npm run test:focused` over the 24 test files in `tests/boundary/providers/native/native*.test.ts` and `src/native/`. 279 pass and 3 fail. The 3 failures are the real-`hdiutil` cases in `nativeDmgArtifactReader.test.ts`; they fail the same way on `main` fd958808 on this Mac, and this change does not touch DMG code. - The before/after CLI run above, with `npm run build:cached` on each checkout. ## Compatibility, safety, and release - Breaking changes or migration steps: none. - Real Hopper/Ghidra, browser, or OS coverage: real `xcrun dyld_info` and `nm` on macOS (arm64) for synthetic dylibs. - Package or release metadata impact: none. - Security, privacy, process, or containment review: none; parser change only. ## Before merge - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff. - [x] The PR has one focused outcome and the title follows `type(scope): outcome`. - [x] Related issue is linked, or the reason for not linking one is stated above. - [x] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [x] User-visible CLI/MCP changes include representative output. - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 01:04:40 +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#5189
No description provided.