[GH-ISSUE #8178] [Design] Disambiguate analysis addresses from file offsets in decompilation #6465

Open
opened 2026-11-01 21:11:21 +00:00 by bsakaguchi · 6 comments
Owner

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

Before submitting

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

Decision needed

How should an analyst unambiguously select a Ghidra analysis address versus a source-file offset for procedure_pseudo_code, and how should a fallback be communicated when a bare hexadecimal value can mean either?

Context and evidence

The current documented contract intentionally tries a 0x value as a procedure address and, if no procedure resolves, falls back to a file offset. The result retains the offset and mapped address in Evidence. Other procedure operations use analysis-address semantics.

Observed on REA 47fa6ab3795d547207001ffb01643c0aa39dcad8, Ghidra 12.1.4 and macOS arm64 through stdio MCP: a self-created x86-64 raw image contained c3, 31 90 bytes, then b8 2a 00 00 00 c3. I opened it with format=raw-binary, architecture=x86_64, and entry_point=0x10; a Ghidra seed explicitly defined other_function at analysis address 0x30 (file offset 0x20). list_procedures returned entries at 0x10 and 0x30 only.

  • procedure_assembly with procedure: "0x20" returned no procedure at that analysis address.
  • procedure_pseudo_code with the same value succeeded and returned other_function at 0x30, because it treated 0x20 as a file offset.
  • Its Evidence locations contained file offset 32 and analysis address 0x30; raw_result.selection recorded the mapping. This is not a hidden or undocumented fallback, but the normalized pseudocode itself did not say why the requested-looking coordinate changed.
  • procedure_pseudo_code with procedure: "ram:0x20" reported no procedure there, avoiding fallback on this fixture. The qualified address syntax is provider-specific, and users must know the address space.

No target bytes were executed or modified. This demonstrates a potentially surprising decision point, not a claim that the implementation violates its documented contract or that every address-space selector works across targets.

Expected interaction with targets

Read a caller-selected binary and query the existing owned Ghidra analysis session. The question concerns input interpretation and Evidence, not target mutation.

Boundary details (optional)

Preserve the caller's coordinate kind and original value, selected mapping and provider address, ambiguity and partial coverage. Avoid returning an unrelated body as though it answered an explicit analysis-address request. A change to existing bare hexadecimal fallback may be breaking; do not infer a coordinate kind silently where the request expressly supplies one.

Options and tradeoffs

Compare an explicit provider-neutral address/file-offset selector, a clearly documented qualified-address path with useful discovery, or a conspicuous inline fallback indicator alongside existing Evidence. Retaining today's fallback offers convenience for old callers but leaves bare hex ambiguous; requiring explicit file-offset: changes behavior. Compare affected CLI/MCP, batch and workflow consumers rather than adding a separate provider-specific decompile tool.

Verification available so far

I have a prototype or observed results

Success criteria and open questions

Exercise a raw image where an absent analysis address is also a valid source offset mapping to a different procedure, exact procedure addresses, explicitly selected file offsets, ambiguous source mappings, qualified address spaces and CLI/MCP parity. Check that errors and returned Evidence preserve the original selection and actual coordinate.

References

Documented behavior: github.com/morluto/rea@47fa6ab379/docs/ghidra-provider.md (loaded-addresses-and-file-offsets)

A follow-up in the same real MCP fixture explicitly selected procedure: "file-offset:0x20" and returned the function at 0x30 with the same file-offset and mapped-address Evidence. ram:0x20 still reported no procedure. This verifies both explicit paths on this one default ram address space.

Batch consumer follow-up

On draft PR #8470 head 3dc41af77444eea9ca2eaf63bf0adb0a31dc9ce2, real Ghidra 12.1.4/JDK 21, Node 24.22.1 and macOS arm64, I used a separate source-owned arm64 Mach-O whose file offset 0x430 maps to analysis entry 0x100000430. In public stdio MCP, both full and compact advertised schemas accepted batch_decompile with ["file-offset:0x430", "0x430", "0x100000430"]. All three items succeeded with identical pseudocode. The explicit and bare offset items had procedure.status: "unknown" because the separate entry-lookup operation did not accept/resolve those values; the exact analysis-address item was resolved. The explicit-offset item's nested lookup failure had a generic invalid-input message, despite the containing batch successfully decompiling that selector.

An individual procedure_pseudo_code for the bare offset retained raw_result.selection with file offset 1072, mapped address and procedure address 0x100000430; the successful batch item did not retain that producer mapping. This illustrates a consumer handoff question alongside #8350, without proving general coordinate equivalence. It also demonstrates why skipping decompilation after entry lookup fails would lose a valid result. The fixture hash was unchanged and the target was not executed. This is one fixture and host, not an agent-selection or performance measurement. Related #8350 tracks per-item producer provenance; #8470 improves lookup recovery but does not solve this batch mapping.

A separate compact stdio MCP control on the same real Ghidra PR head saved the successful explicit-offset batch before making any individual decompile call. The batch-only export contained one record and no separate decompile source or link. After explicitly calling procedure_pseudo_code for the same selector, a second export contained that separate observation with the 1072 → 0x100000430 mapping. Each bundle was imported into a fresh target-free server; the batch result survived exactly, and the mapping was present only in the second bundle. The batch did not link to the separately requested observation. Target and bundle hashes remained unchanged during inspection. This is a manually requested extra observation and offline handoff on one fixture, not an automatic recovery, agent trial, or proof about all batch sources.

Ambiguous PE mapping follow-up

On REA #8768 working tree based on 9ebbe67e, real Ghidra 12.1.4 and a deliberately modified PE where file offset 0x400 maps to both 0x180001000 and 0x180002000, explicit offset decompilation refused the ambiguous mapping. Selecting bare 0x180001000 through the CLI decompiled a function; selecting bare 0x180002000 failed after the automatic file-offset fallback, reporting that 0x180002000 had no loaded source mapping. In the same fixture, MCP resolve_containing_procedure returned found: false and inspect_native_instruction returned status: data at 0x180002000 in both output profiles. A separate CLI request using ram:0x180002000 avoided fallback and reported no procedure at that requested address.

The unexecuted, synthetic PE SHA-256 stayed 9288ce1a1a3e4aae2a47ba0fc97c466c6258285ddff1cf4efdb62cfb52151595. This demonstrates an unhelpful failure after fallback, not successful selection of a different function or proof that qualified syntax works across address spaces. #8772 and #8768 address ambiguity reporting; they do not change this pre-existing fallback.

Discovering the qualified space for the PE control

In the same synthetic PE and real Ghidra sessions, inspect_native_load_image with complete detail returned Ghidra's observed default_address_space: "ram" in both output profiles, while independent PE load-image verification was unsupported. Using that observed space to construct ram:0x180002000 for MCP procedure_pseudo_code returned the no-procedure-at-requested-address issue without attempting the bare-hex file-offset fallback. The target digest remained unchanged. This supplies one provider-observed route to a strict qualified selector; it is not independently verified PE load-image equivalence and does not prove how every non-default or encoded address space should be selected. The returned no-procedure error still has generic input-schema remediation.

The same real PE control also returned observations.default_address_space: "ram" when inspect_native_load_image omitted detail in both MCP output profiles; this result was complete Evidence rather than a summary in that invocation. Independent verification remained unsupported. This is one fixture and does not establish default delivery for every result size or transport.

Same numeric selector on a Mach-O

On a second source-owned arm64 Mach-O with real Ghidra 12.1.4, inspect_native_load_image reported the observed default space ram. In both MCP output profiles, procedure_pseudo_code with bare 0x328 succeeded via raw_result.selection showing file offset 808 mapped to procedure 0x100000328, while ram:0x328 returned no procedure at the requested analysis address. This demonstrates a different successful function selection under fallback on this fixture; the qualified form preserved analysis-coordinate intent. The target remained unexecuted and SHA-256 stayed ae0f2a349b22332113ab60ab4c78024eb18153c76bdeefb01c4b1d89a26c0003. It does not establish a general address conversion or coverage for non-default spaces. PR #8768 advertises this existing qualified route without changing the fallback.

Packed PE address control

On #8470 head 842cf7cd155e1247f12c0743ed04144e6a6b152f, Ghidra 12.1.4/JDK 21, Node 24.22.1 and macOS arm64, a self-built UPX 5.3.0 packed x86-64 PE was opened through public MCP. Asking procedure_pseudo_code for bare 0x140001000 in compact and full profiles fell back to treating that value as a file offset, then reported no source mapping and recommended inspecting mappings. Asking for ram:0x140001000 instead reported no procedure at that analysis address and recommended inventory discovery. In a separately unpacked copy, the bare selector decompiled a function at that address through Ghidra in both profiles (tested on the Kuna 1.990 validation branch). The packed file SHA-256 remained 7d2b97acb6ecdba9ff9422fbb13516f41251f90faff3670cc878eb0a6f180dfa; neither file was executed or modified. This demonstrates the impact of coordinate fallback on a packed-versus-unpacked handoff, not automatic unpack detection or a general claim about address spaces. The control used the known ram space; it did not independently inspect the space in this fixture.

Originally created by @morluto on GitHub (Oct 30, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/8178 ### Before submitting - [x] I searched the open and closed issues and pull requests for a related discussion. - [x] I understand the [contribution scope](https://github.com/morluto/rea/blob/main/CONTRIBUTING.md#contribution-scope) must be reviewed before integration; open questions are welcome here. - [x] This discussion does not include credentials, private samples, or sensitive target data. ### Decision needed How should an analyst unambiguously select a Ghidra analysis address versus a source-file offset for `procedure_pseudo_code`, and how should a fallback be communicated when a bare hexadecimal value can mean either? ### Context and evidence The current documented contract intentionally tries a `0x` value as a procedure address and, if no procedure resolves, falls back to a file offset. The result retains the offset and mapped address in Evidence. Other procedure operations use analysis-address semantics. Observed on REA `47fa6ab3795d547207001ffb01643c0aa39dcad8`, Ghidra 12.1.4 and macOS arm64 through stdio MCP: a self-created x86-64 raw image contained `c3`, 31 `90` bytes, then `b8 2a 00 00 00 c3`. I opened it with `format=raw-binary`, `architecture=x86_64`, and `entry_point=0x10`; a Ghidra seed explicitly defined `other_function` at analysis address `0x30` (file offset `0x20`). `list_procedures` returned entries at `0x10` and `0x30` only. - `procedure_assembly` with `procedure: "0x20"` returned no procedure at that analysis address. - `procedure_pseudo_code` with the same value succeeded and returned `other_function` at `0x30`, because it treated `0x20` as a file offset. - Its Evidence locations contained file offset 32 and analysis address `0x30`; `raw_result.selection` recorded the mapping. This is not a hidden or undocumented fallback, but the normalized pseudocode itself did not say why the requested-looking coordinate changed. - `procedure_pseudo_code` with `procedure: "ram:0x20"` reported no procedure there, avoiding fallback on this fixture. The qualified address syntax is provider-specific, and users must know the address space. No target bytes were executed or modified. This demonstrates a potentially surprising decision point, not a claim that the implementation violates its documented contract or that every address-space selector works across targets. ### Expected interaction with targets Read a caller-selected binary and query the existing owned Ghidra analysis session. The question concerns input interpretation and Evidence, not target mutation. ### Boundary details (optional) Preserve the caller's coordinate kind and original value, selected mapping and provider address, ambiguity and partial coverage. Avoid returning an unrelated body as though it answered an explicit analysis-address request. A change to existing bare hexadecimal fallback may be breaking; do not infer a coordinate kind silently where the request expressly supplies one. ### Options and tradeoffs Compare an explicit provider-neutral address/file-offset selector, a clearly documented qualified-address path with useful discovery, or a conspicuous inline fallback indicator alongside existing Evidence. Retaining today's fallback offers convenience for old callers but leaves bare hex ambiguous; requiring explicit `file-offset:` changes behavior. Compare affected CLI/MCP, batch and workflow consumers rather than adding a separate provider-specific decompile tool. ### Verification available so far I have a prototype or observed results ### Success criteria and open questions Exercise a raw image where an absent analysis address is also a valid source offset mapping to a different procedure, exact procedure addresses, explicitly selected file offsets, ambiguous source mappings, qualified address spaces and CLI/MCP parity. Check that errors and returned Evidence preserve the original selection and actual coordinate. ### References Documented behavior: https://github.com/morluto/rea/blob/47fa6ab3795d547207001ffb01643c0aa39dcad8/docs/ghidra-provider.md#loaded-addresses-and-file-offsets A follow-up in the same real MCP fixture explicitly selected `procedure: "file-offset:0x20"` and returned the function at `0x30` with the same file-offset and mapped-address Evidence. `ram:0x20` still reported no procedure. This verifies both explicit paths on this one default `ram` address space. ### Batch consumer follow-up On draft PR #8470 head `3dc41af77444eea9ca2eaf63bf0adb0a31dc9ce2`, real Ghidra 12.1.4/JDK 21, Node 24.22.1 and macOS arm64, I used a separate source-owned arm64 Mach-O whose file offset `0x430` maps to analysis entry `0x100000430`. In public stdio MCP, both full and compact advertised schemas accepted `batch_decompile` with `["file-offset:0x430", "0x430", "0x100000430"]`. All three items succeeded with identical pseudocode. The explicit and bare offset items had `procedure.status: "unknown"` because the separate entry-lookup operation did not accept/resolve those values; the exact analysis-address item was resolved. The explicit-offset item's nested lookup failure had a generic invalid-input message, despite the containing batch successfully decompiling that selector. An individual `procedure_pseudo_code` for the bare offset retained `raw_result.selection` with file offset 1072, mapped address and procedure address `0x100000430`; the successful batch item did not retain that producer mapping. This illustrates a consumer handoff question alongside #8350, without proving general coordinate equivalence. It also demonstrates why skipping decompilation after entry lookup fails would lose a valid result. The fixture hash was unchanged and the target was not executed. This is one fixture and host, not an agent-selection or performance measurement. Related #8350 tracks per-item producer provenance; #8470 improves lookup recovery but does not solve this batch mapping. A separate compact stdio MCP control on the same real Ghidra PR head saved the successful explicit-offset batch before making any individual decompile call. The batch-only export contained one record and no separate decompile source or link. After explicitly calling `procedure_pseudo_code` for the same selector, a second export contained that separate observation with the 1072 → `0x100000430` mapping. Each bundle was imported into a fresh target-free server; the batch result survived exactly, and the mapping was present only in the second bundle. The batch did not link to the separately requested observation. Target and bundle hashes remained unchanged during inspection. This is a manually requested extra observation and offline handoff on one fixture, not an automatic recovery, agent trial, or proof about all batch sources. ### Ambiguous PE mapping follow-up On REA #8768 working tree based on `9ebbe67e`, real Ghidra 12.1.4 and a deliberately modified PE where file offset `0x400` maps to both `0x180001000` and `0x180002000`, explicit offset decompilation refused the ambiguous mapping. Selecting bare `0x180001000` through the CLI decompiled a function; selecting bare `0x180002000` failed after the automatic file-offset fallback, reporting that `0x180002000` had no loaded source mapping. In the same fixture, MCP `resolve_containing_procedure` returned `found: false` and `inspect_native_instruction` returned `status: data` at `0x180002000` in both output profiles. A separate CLI request using `ram:0x180002000` avoided fallback and reported no procedure at that requested address. The unexecuted, synthetic PE SHA-256 stayed `9288ce1a1a3e4aae2a47ba0fc97c466c6258285ddff1cf4efdb62cfb52151595`. This demonstrates an unhelpful failure after fallback, not successful selection of a different function or proof that qualified syntax works across address spaces. #8772 and #8768 address ambiguity reporting; they do not change this pre-existing fallback. ### Discovering the qualified space for the PE control In the same synthetic PE and real Ghidra sessions, `inspect_native_load_image` with complete detail returned Ghidra's observed `default_address_space: "ram"` in both output profiles, while independent PE load-image verification was `unsupported`. Using that observed space to construct `ram:0x180002000` for MCP `procedure_pseudo_code` returned the no-procedure-at-requested-address issue without attempting the bare-hex file-offset fallback. The target digest remained unchanged. This supplies one provider-observed route to a strict qualified selector; it is not independently verified PE load-image equivalence and does not prove how every non-default or encoded address space should be selected. The returned no-procedure error still has generic input-schema remediation. The same real PE control also returned `observations.default_address_space: "ram"` when `inspect_native_load_image` omitted `detail` in both MCP output profiles; this result was complete Evidence rather than a summary in that invocation. Independent verification remained `unsupported`. This is one fixture and does not establish default delivery for every result size or transport. ### Same numeric selector on a Mach-O On a second source-owned arm64 Mach-O with real Ghidra 12.1.4, `inspect_native_load_image` reported the observed default space `ram`. In both MCP output profiles, `procedure_pseudo_code` with bare `0x328` succeeded via `raw_result.selection` showing file offset 808 mapped to procedure `0x100000328`, while `ram:0x328` returned no procedure at the requested analysis address. This demonstrates a different successful function selection under fallback on this fixture; the qualified form preserved analysis-coordinate intent. The target remained unexecuted and SHA-256 stayed `ae0f2a349b22332113ab60ab4c78024eb18153c76bdeefb01c4b1d89a26c0003`. It does not establish a general address conversion or coverage for non-default spaces. PR #8768 advertises this existing qualified route without changing the fallback. ### Packed PE address control On #8470 head `842cf7cd155e1247f12c0743ed04144e6a6b152f`, Ghidra 12.1.4/JDK 21, Node 24.22.1 and macOS arm64, a self-built UPX 5.3.0 packed x86-64 PE was opened through public MCP. Asking `procedure_pseudo_code` for bare `0x140001000` in compact and full profiles fell back to treating that value as a file offset, then reported no source mapping and recommended inspecting mappings. Asking for `ram:0x140001000` instead reported no procedure at that analysis address and recommended inventory discovery. In a separately unpacked copy, the bare selector decompiled a function at that address through Ghidra in both profiles (tested on the Kuna 1.990 validation branch). The packed file SHA-256 remained `7d2b97acb6ecdba9ff9422fbb13516f41251f90faff3670cc878eb0a6f180dfa`; neither file was executed or modified. This demonstrates the impact of coordinate fallback on a packed-versus-unpacked handoff, not automatic unpack detection or a general claim about address spaces. The control used the known `ram` space; it did not independently inspect the space in this fixture.
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

Looping in @gensokyo-dev @kaoru0822-kitauji @N0zoM1z0 for visibility on the Ghidra, Kuna, and REA tool-design investigation.

<!-- gh-comment-id:6538787644 --> @morluto commented on GitHub (Oct 31, 2026): Looping in @gensokyo-dev @kaoru0822-kitauji @N0zoM1z0 for visibility on the Ghidra, Kuna, and REA tool-design investigation.
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

Explicit file offset across a rebased project

On REA b43c329df779708e43ae297f498a844e45d9c923, real Ghidra 12.1.4 stdio MCP analyzed the same source-owned arm64 Mach-O in two sessions: a fresh import and a caller-selected, read-only project whose image base had been changed from 0x100000000 to 0x180000000 before the REA session. procedure_pseudo_code({procedure:"file-offset:0x2d8"}) returned the same body in each. Its recorded selection mapped source offset 728 to 0x1000002d8 in the fresh import and 0x1800002d8 in the selected project. Bare 0x2d8 reached the same results via the documented fallback.

For the rebased project, rea decompile <target> --file-offset 0x2d8 --provider ghidra --format json --full-output returned the same Evidence ID and explicit offset/mapped-address/procedure-address selection as MCP. This gives a concrete supported handoff when the caller has an authoritative source offset. As controls, procedure_address and procedure_assembly rejected file-offset:0x2d8; their selectors have different semantics, so the mapped analysis address from the decompile Evidence is needed for those operations.

The target SHA-256 and ten source-project regular-file hashes remained unchanged through the REA calls. The target was not executed. This is one non-overlapping mapping and one rebased project; ambiguous mappings, altered target bytes and other providers are not established by this control.

<!-- gh-comment-id:6541580601 --> @morluto commented on GitHub (Oct 31, 2026): ### Explicit file offset across a rebased project On REA `b43c329df779708e43ae297f498a844e45d9c923`, real Ghidra 12.1.4 stdio MCP analyzed the same source-owned arm64 Mach-O in two sessions: a fresh import and a caller-selected, read-only project whose image base had been changed from `0x100000000` to `0x180000000` before the REA session. `procedure_pseudo_code({procedure:"file-offset:0x2d8"})` returned the same body in each. Its recorded selection mapped source offset `728` to `0x1000002d8` in the fresh import and `0x1800002d8` in the selected project. Bare `0x2d8` reached the same results via the documented fallback. For the rebased project, `rea decompile <target> --file-offset 0x2d8 --provider ghidra --format json --full-output` returned the same Evidence ID and explicit offset/mapped-address/procedure-address selection as MCP. This gives a concrete supported handoff when the caller has an authoritative source offset. As controls, `procedure_address` and `procedure_assembly` rejected `file-offset:0x2d8`; their selectors have different semantics, so the mapped analysis address from the decompile Evidence is needed for those operations. The target SHA-256 and ten source-project regular-file hashes remained unchanged through the REA calls. The target was not executed. This is one non-overlapping mapping and one rebased project; ambiguous mappings, altered target bytes and other providers are not established by this control.
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

Real Ghidra duplicate-file-mapping control

In a disposable Ghidra 12.1.4 project for a source-owned arm64 Mach-O, a headless script created a second initialized executable block backed by the same original FileBytes at source offset 0x2d8, then defined a second function there. Ghidra independently reported mappings [0x1000002d8, 0x110000000]. This deliberately edits the disposable project before testing; REA subsequently selects it read-only.

On REA b43c329df779708e43ae297f498a844e45d9c923 through real stdio MCP:

  • Both procedure_pseudo_code({procedure:"file-offset:0x2d8"}) and bare "0x2d8" refused ambiguity and listed both candidate analysis addresses; neither chose a function.
  • Exact addresses selected entry and audit_duplicate separately. address_to_file_offset returned 728 for each address. inspect_native_load_image retained both initialized, non-overlay mappings with the same source-file index and offset; its independent verification status was unsupported for Mach-O.
  • Real rea decompile <target> --file-offset 0x2d8 --provider ghidra --format json --full-output also exited 1. The CLI's top-level message was generic invalid input, while its nested issue named both candidates and advised selecting an explicit analysis address.

The target SHA-256 and all ten source-project regular-file hashes were unchanged through REA MCP and CLI calls. No target was executed. This verifies one genuine same-source, two-loaded-address ambiguity and the exact-address recovery; it does not establish behavior for overlays, multiple distinct source files, or all address spaces.

<!-- gh-comment-id:6541593273 --> @morluto commented on GitHub (Oct 31, 2026): ### Real Ghidra duplicate-file-mapping control In a disposable Ghidra 12.1.4 project for a source-owned arm64 Mach-O, a headless script created a second initialized executable block backed by the **same original FileBytes** at source offset `0x2d8`, then defined a second function there. Ghidra independently reported mappings `[0x1000002d8, 0x110000000]`. This deliberately edits the disposable project before testing; REA subsequently selects it read-only. On REA `b43c329df779708e43ae297f498a844e45d9c923` through real stdio MCP: - Both `procedure_pseudo_code({procedure:"file-offset:0x2d8"})` and bare `"0x2d8"` refused ambiguity and listed both candidate analysis addresses; neither chose a function. - Exact addresses selected `entry` and `audit_duplicate` separately. `address_to_file_offset` returned `728` for each address. `inspect_native_load_image` retained both initialized, non-overlay mappings with the same source-file index and offset; its independent verification status was unsupported for Mach-O. - Real `rea decompile <target> --file-offset 0x2d8 --provider ghidra --format json --full-output` also exited 1. The CLI's top-level message was generic invalid input, while its nested issue named both candidates and advised selecting an explicit analysis address. The target SHA-256 and all ten source-project regular-file hashes were unchanged through REA MCP and CLI calls. No target was executed. This verifies one genuine same-source, two-loaded-address ambiguity and the exact-address recovery; it does not establish behavior for overlays, multiple distinct source files, or all address spaces.
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

Distinct source-file identity control

I created a separate disposable Ghidra 12.1.4 project in which the admitted arm64 Mach-O maps its original file offset 0x2d8 at 0x1000002d8, while a different 1,024-byte foreign.bin maps its own offset 0x2d8 at 0x110000000. The foreign block and its function were explicitly authored before the REA session; Ghidra independently confirmed one mapping for each FileBytes object.

On REA b43c329df779708e43ae297f498a844e45d9c923, real Ghidra MCP procedure_pseudo_code({procedure:"file-offset:0x2d8"}) selected the target-backed entry at 0x1000002d8, not the foreign function. An explicit analysis-address request selected audit_foreign and returned its distinct 0x2a body. address_to_file_offset accepted the target address as offset 728 and refused the foreign address with 0 authoritative target-file mappings. inspect_native_load_image preserved both source digests and their distinct source-file indices; its independent Mach-O verification was unsupported.

The equivalent CLI decompile --file-offset 0x2d8 returned the same Evidence ID and target selection as MCP; CLI address-to-file-offset likewise refused the foreign address. This contrasts with the same-FileBytes duplicate mapping above, where REA correctly refused ambiguity. Target and ten project-file hashes stayed unchanged during the REA calls; no target was executed. This is one different-source control, not proof of all partial-source, overlay or changed-project cases.

<!-- gh-comment-id:6541651151 --> @morluto commented on GitHub (Oct 31, 2026): ### Distinct source-file identity control I created a separate disposable Ghidra 12.1.4 project in which the admitted arm64 Mach-O maps its original file offset `0x2d8` at `0x1000002d8`, while a different 1,024-byte `foreign.bin` maps its own offset `0x2d8` at `0x110000000`. The foreign block and its function were explicitly authored before the REA session; Ghidra independently confirmed one mapping for each FileBytes object. On REA `b43c329df779708e43ae297f498a844e45d9c923`, real Ghidra MCP `procedure_pseudo_code({procedure:"file-offset:0x2d8"})` selected the target-backed `entry` at `0x1000002d8`, not the foreign function. An explicit analysis-address request selected `audit_foreign` and returned its distinct `0x2a` body. `address_to_file_offset` accepted the target address as offset `728` and refused the foreign address with `0 authoritative target-file mappings`. `inspect_native_load_image` preserved both source digests and their distinct source-file indices; its independent Mach-O verification was unsupported. The equivalent CLI `decompile --file-offset 0x2d8` returned the same Evidence ID and target selection as MCP; CLI `address-to-file-offset` likewise refused the foreign address. This contrasts with the same-FileBytes duplicate mapping above, where REA correctly refused ambiguity. Target and ten project-file hashes stayed unchanged during the REA calls; no target was executed. This is one different-source control, not proof of all partial-source, overlay or changed-project cases.
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

A separate real CLI handoff detail for selector design: UI trace and address_to_file_offset report file offsets as JSON numbers, but rea decompile --file-offset accepts only a 0x-prefixed hexadecimal string. On the owned UI fixture, passing decimal 2208 failed argument validation; converting that same offset to 0x8a0 succeeded and produced the same handler body as its explicit analysis address. rea decompile --schema advertises the hex pattern, while --help describes an offset without stating the required format. A second decimal input failed the same way.

This is a representation/discoverability question, not evidence that REA confuses the supplied offset's coordinate kind. It is independent of the incorrect UI-node offset documented in #8198. Consider a clear accepted-format example or a lossless numeric handoff while retaining explicit coordinate semantics; no autonomous-agent failure rate was measured.

<!-- gh-comment-id:6542308959 --> @morluto commented on GitHub (Oct 31, 2026): A separate real CLI handoff detail for selector design: UI trace and `address_to_file_offset` report file offsets as JSON numbers, but `rea decompile --file-offset` accepts only a `0x`-prefixed hexadecimal string. On the owned UI fixture, passing decimal `2208` failed argument validation; converting that same offset to `0x8a0` succeeded and produced the same handler body as its explicit analysis address. `rea decompile --schema` advertises the hex pattern, while `--help` describes an offset without stating the required format. A second decimal input failed the same way. This is a representation/discoverability question, not evidence that REA confuses the supplied offset's coordinate kind. It is independent of the incorrect UI-node offset documented in #8198. Consider a clear accepted-format example or a lossless numeric handoff while retaining explicit coordinate semantics; no autonomous-agent failure rate was measured.
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

A separate selected-project control shows why source-file and provider address selectors must stay explicit even when both are mapped: https://github.com/morluto/rea/issues/8328#issuecomment-6547720199

On a page-aligned rebased Mach-O project, bare 0x2000 intentionally selected the existing Ghidra function at analysis address 0x2000; file-offset:0x2000 selected a different function at analysis address 0x3000, with the mapping retained in raw Evidence. The CLI --file-offset route agreed with MCP. Neither result is a violation of the existing selector contract. The example illustrates the danger of passing a separately observed file-derived VM value as an analysis address without proving its coordinate mapping; the value happened to equal a file offset only in this constructed fixture.

<!-- gh-comment-id:6547721703 --> @morluto commented on GitHub (Oct 31, 2026): A separate selected-project control shows why source-file and provider address selectors must stay explicit even when both are mapped: https://github.com/morluto/rea/issues/8328#issuecomment-6547720199 On a page-aligned rebased Mach-O project, bare `0x2000` intentionally selected the existing Ghidra function at analysis address `0x2000`; `file-offset:0x2000` selected a different function at analysis address `0x3000`, with the mapping retained in raw Evidence. The CLI `--file-offset` route agreed with MCP. Neither result is a violation of the existing selector contract. The example illustrates the danger of passing a separately observed file-derived VM value as an analysis address without proving its coordinate mapping; the value happened to equal a file offset only in this constructed fixture.
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#6465
No description provided.