* feat(ghidra): admit soft-float MIPS32r2 ELF with integer-register float storage Soft-float o32 executables (Linux kernel builds, bare-metal firmware) were refused because their ABI flags record declares no CPR1 and FP ABI 3. Ghidra's MIPS32 compiler specifications model only hard-float o32, so typed float/double signatures (DWARF) would be bound to $f12/$f14/$f0 and the decompiler loses the real dataflow: `return sel ? a : b` decompiled to `return in_f0;`, and callers passed phantom FPU inputs. Admit ELF32 ET_EXEC o32 MIPS32r2 targets whose ABI record declares soft-float with no CPR1, and commit `mips_soft_float_storage: o32-integer-registers-v1` in their analysis profile only (hard-float profile digests are unchanged). ReaGhidraMipsSoftFloat.java runs after auto-analysis and, for each function with a float/double formal type, either keeps the types with the storage Ghidra's o32 model assigns to a same-size integer (pair alignment, stack spill), or, for auto-parameters such as hidden struct-return pointers, swaps float types for same-size integers under dynamic storage. No function is left on hard-float storage. The report is written last; the provider client requires it exactly when the profile commits the policy and refuses the session before any operation otherwise. Verification: real Ghidra 12.1.4 lane (verify:ghidra:mips) now builds hard- and soft-float fixtures in both byte orders and checks exact parameter/return storage through inspect_native_api, caller constants, snapshot profile and Evidence outcome; test:fast, verify:package and an installed-tarball soft-float run pass. * fix(ghidra): model soft-float MIPS as a prototype for every typed callable Review feedback on #1949: rewriting storage per Function left external functions and indirect calls through typed function pointers on Ghidra's hard-float o32 prototype, which still read $f12/$f14/$f0 while the session reported success. A negative control confirms it: without the change, `(*rea_mips_soft_callback)(8.5f, 9.5f, sel)` decompiles to `(*rea_mips_soft_callback)(in_f12, in_f14, sel)`. ReaGhidraMipsSoftFloat.java now derives a `__rea_soft_float` prototype from the program's own default o32 model (encoded, float pentries and float-consuming rules removed, so byte-order-specific aggregate rules and the hidden struct-return pointer are kept), installs it as a specification extension, and assigns it with dynamic storage to every non-thunk function, external function and function-pointer definition whose formal types use float or double. Thunks follow their targets. Float types are kept everywhere, so the integer-retype fallback is removed. The script fails without writing its report if the derived prototype or any reassigned function still uses an FPU register. The policy becomes `o32-soft-float-prototype-v1` with a {functions, definitions} report. The soft-float fixture adds an indirect call through a typed pointer; the real-Ghidra lane requires its constants with no FPU inputs and the struct return's `float scale` in $a1. * test(ghidra): pin ODDSPREG acceptance for soft-float MIPS ABI records Review on #1949 suggested requiring flags1 == 0 for soft-float, since MIPS_AFL_FLAGS1_ODDSPREG names CPR1 registers. GCC 5.2 kernel modules, LLVM/Rust firmware and Clang -msoft-float objects all record flags1 = 1 with FP ABI soft-float: it is the MIPS32 o32 toolchain default and selects nothing without CPR1. Keep accepting it, say why at the check, and test that soft-float with ODDSPREG is admitted while any other general flag is refused.
54 lines
2.0 KiB
C
54 lines
2.0 KiB
C
/* Soft-float o32 storage fixture; linked with mips.c, never executed by REA.
|
|
* Without an FPU, float/double values travel in integer registers and stack
|
|
* slots: pick uses $a0/$a1/$a2 and returns in $v0, wide uses the $a0:$a1 pair,
|
|
* and mixed aligns d to $a2:$a3 so f and e spill to the stack at 16 and 24.
|
|
* structured returns through a hidden $a0 pointer, moving scale to $a1. The
|
|
* typed function pointer makes the caller's indirect call use a prototype that
|
|
* belongs to no function. No arithmetic, so no compiler-rt helpers are needed.
|
|
*/
|
|
volatile unsigned int rea_mips_soft_sink;
|
|
|
|
__attribute__((noinline)) float rea_mips_soft_pick(float a, float b, int sel)
|
|
{
|
|
return sel ? a : b;
|
|
}
|
|
|
|
__attribute__((noinline)) double rea_mips_soft_wide(double a, int sel)
|
|
{
|
|
return sel ? a : 0.5;
|
|
}
|
|
|
|
__attribute__((noinline)) double rea_mips_soft_mixed(int i, double d, float f, double e)
|
|
{
|
|
rea_mips_soft_sink = *(volatile unsigned int *)&f;
|
|
return i ? d : e;
|
|
}
|
|
|
|
struct rea_mips_soft_pair {
|
|
double first;
|
|
double second;
|
|
};
|
|
|
|
__attribute__((noinline)) struct rea_mips_soft_pair rea_mips_soft_struct(float scale, int sel)
|
|
{
|
|
struct rea_mips_soft_pair pair = { 0.25, 0.75 };
|
|
rea_mips_soft_sink = *(volatile unsigned int *)&scale;
|
|
if (sel)
|
|
pair.first = 1.25;
|
|
return pair;
|
|
}
|
|
|
|
/* Volatile so the indirect call through this typed pointer is not folded. */
|
|
float (*volatile rea_mips_soft_callback)(float, float, int) = rea_mips_soft_pick;
|
|
|
|
__attribute__((noinline)) unsigned int rea_mips_soft_caller(int sel)
|
|
{
|
|
float picked = rea_mips_soft_pick(1.5f, 2.5f, sel);
|
|
double wide = rea_mips_soft_wide(3.25, sel);
|
|
double mixed = rea_mips_soft_mixed(sel, 4.75, 5.5f, 6.125);
|
|
struct rea_mips_soft_pair pair = rea_mips_soft_struct(7.5f, sel);
|
|
float called = rea_mips_soft_callback(8.5f, 9.5f, sel);
|
|
return *(unsigned int *)&picked ^ *(unsigned int *)&wide ^ *(unsigned int *)&mixed ^
|
|
*(unsigned int *)&pair.first ^ *(unsigned int *)&called;
|
|
}
|