[GH-ISSUE #1937] [Feature] Admit soft-float MIPS32r2 ELF through Ghidra with integer-register float storage #644

Closed
opened 2026-10-15 18:26:53 +00:00 by bsakaguchi · 1 comment
Owner

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

Before submitting

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

Area

Native analysis or decompilation

Current limitation

The Ghidra MIPS lane from #1362 refuses ELF32 o32 MIPS32r2 executables whose ABI flags record declares soft-float (cpr1_size 0, fp_abi 3):

This Ghidra MIPS lane admits double-precision or FPXX ABI declarations only; soft-float, FP64 and unspecified modes need separate verification.

Relaxing the check alone would be wrong. Measured with Ghidra 12.1.4 on a source-owned soft-float fixture with DWARF: Ghidra's MIPS32 compiler specifications model hard-float o32 only, so typed float/double parameters bind to $f12/$f14 and returns to $f0, while soft-float code passes them in $a0..$a3/stack and returns in $v0/$v1.

float pick(float a, float b, int sel) { return sel ? a : b; }

decompiles to return in_f0; (the select is lost), and callers pass phantom in_f12/in_f14 inputs. Without DWARF the same code decompiles correctly, so decoding is fine; the fault is typed float storage.

Use cases

Soft-float is the norm for Linux MIPS kernels and modules and for many bare-metal builds (for example Rust mipsel-mti-none-elf firmware). These are refused today.

Desired outcome and success criteria

  • Soft-float o32 MIPS32r2 executables are admitted; inconsistent records (soft-float with CPR1, hard-float without CPR1), single-float, FP64 and unspecified ABIs stay refused.
  • Typed float/double parameters and returns are bound to soft-float o32 storage, including $a2:$a3 double alignment, stack spill and hidden struct-return pointers, in both byte orders.
  • The storage policy is committed in the analysis profile; the session fails closed if it was not applied; Evidence carries the policy and outcome as limitations.
  • Hard-float profiles are unchanged.

Authority boundaries and non-goals

No new Ghidra language/cspec, no emulation, no FP64/NaN2008/single-float, no Hopper/IDA changes.

Possible direction

A post-analysis Ghidra script (after ReaGhidraNoReturnFix) that, for each function with a float/double formal type, keeps the declared types but commits the storage Ghidra's own o32 prototype model assigns to a same-size integer; functions with auto-parameters fall back to same-size integer types under dynamic storage. It records an outcome in PROGRAM_INFO that the bridge reports and the provider client requires exactly when the profile commits the policy.

I have an implementation with real-Ghidra verification (soft-float fixtures in verify:ghidra:mips, both byte orders, exact inspect_native_api storage checks) on https://github.com/AurelioB/rea/tree/feat/mips-soft-float and can open a PR if this direction is acceptable.

References

#1362, #1620, #1898

Originally created by @AurelioB on GitHub (Oct 11, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/1937 ### Before submitting - [x] I searched the open and closed issues and pull requests for a related proposal. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Native analysis or decompilation ### Current limitation The Ghidra MIPS lane from #1362 refuses ELF32 o32 MIPS32r2 executables whose ABI flags record declares soft-float (`cpr1_size 0`, `fp_abi 3`): > This Ghidra MIPS lane admits double-precision or FPXX ABI declarations only; soft-float, FP64 and unspecified modes need separate verification. Relaxing the check alone would be wrong. Measured with Ghidra 12.1.4 on a source-owned soft-float fixture with DWARF: Ghidra's MIPS32 compiler specifications model hard-float o32 only, so typed `float`/`double` parameters bind to `$f12/$f14` and returns to `$f0`, while soft-float code passes them in `$a0..$a3`/stack and returns in `$v0/$v1`. ```c float pick(float a, float b, int sel) { return sel ? a : b; } ``` decompiles to `return in_f0;` (the select is lost), and callers pass phantom `in_f12`/`in_f14` inputs. Without DWARF the same code decompiles correctly, so decoding is fine; the fault is typed float storage. ### Use cases Soft-float is the norm for Linux MIPS kernels and modules and for many bare-metal builds (for example Rust `mipsel-mti-none-elf` firmware). These are refused today. ### Desired outcome and success criteria - Soft-float o32 MIPS32r2 executables are admitted; inconsistent records (soft-float with CPR1, hard-float without CPR1), single-float, FP64 and unspecified ABIs stay refused. - Typed float/double parameters and returns are bound to soft-float o32 storage, including `$a2:$a3` double alignment, stack spill and hidden struct-return pointers, in both byte orders. - The storage policy is committed in the analysis profile; the session fails closed if it was not applied; Evidence carries the policy and outcome as limitations. - Hard-float profiles are unchanged. ### Authority boundaries and non-goals No new Ghidra language/cspec, no emulation, no FP64/NaN2008/single-float, no Hopper/IDA changes. ### Possible direction A post-analysis Ghidra script (after `ReaGhidraNoReturnFix`) that, for each function with a float/double formal type, keeps the declared types but commits the storage Ghidra's own o32 prototype model assigns to a same-size integer; functions with auto-parameters fall back to same-size integer types under dynamic storage. It records an outcome in `PROGRAM_INFO` that the bridge reports and the provider client requires exactly when the profile commits the policy. I have an implementation with real-Ghidra verification (soft-float fixtures in `verify:ghidra:mips`, both byte orders, exact `inspect_native_api` storage checks) on https://github.com/AurelioB/rea/tree/feat/mips-soft-float and can open a PR if this direction is acceptable. ### References #1362, #1620, #1898
bsakaguchi 2026-10-15 18:26:53 +00:00
Author
Owner

@AurelioB commented on GitHub (Oct 11, 2026):

Implemented in #1949. Review led to a change of approach from the "Possible direction" above: rather than rewriting storage per function, the script now derives a program-specific __rea_soft_float prototype from the default o32 model (float entries and rules removed) and assigns it to functions, external functions and typed function-pointer definitions, so indirect and external calls are covered too.

<!-- gh-comment-id:6113448606 --> @AurelioB commented on GitHub (Oct 11, 2026): Implemented in #1949. Review led to a change of approach from the "Possible direction" above: rather than rewriting storage per function, the script now derives a program-specific `__rea_soft_float` prototype from the default o32 model (float entries and rules removed) and assigns it to functions, external functions and typed function-pointer definitions, so indirect and external calls are covered too.
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#644
No description provided.