mirror of
https://github.com/morluto/rea.git
synced 2026-11-08 07:46:28 +00:00
* 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. * feat(ghidra): admit relocatable MIPS32r2 ELF objects Linux MIPS kernel modules (.ko) and other ELF32 o32 MIPS32r2 ET_REL objects were refused by the Ghidra MIPS lane. Ghidra's ELF loader already places each allocated section and applies REL relocations; this admits ET_REL alongside ET_EXEC (ET_DYN and PRX stay refused) under its own support lane, `elf32-rel-o32-arch32r2-standard-v1`, so executable targets keep theirs. Relocatable sessions carry limitations: addresses are analysis coordinates, undefined symbols live in Ghidra's EXTERNAL block, and HI16 relocations report PARTIAL because Ghidra applies them with their paired LO16. Verification: new `verify:ghidra:mips-relocatable` real-Ghidra lane builds -c -G0 objects in both byte orders plus a soft-float -mlong-calls module variant. GNU readelf supplies sections, symbols and REL entries; every relocation in an allocated section must encode S + A in the loaded word (S from Ghidra placement, A from original bytes), with HI16/LO16 checked as the effective address each LO16 forms, and coverage of a shared HI16, a LO16 carry, external code/data and R_MIPS_32 is asserted. The executable lane, unit suites pass, and on 30 real .ko files 54,972 relocations matched (93 reference empty sections and are not checkable). * fix(ghidra): verify every MIPS HI16 and archive-typed soft-float externals Review feedback on #1950: - The relocation oracle kept one pending HI16, so a second HI16 before the LO16 that resolves both overwrote the first, and unrelated relocations cleared it; the earlier site's high half and carry were never compared with S + A. Every HI16 now waits for the next LO16 of its symbol, which checks all of them; later LO16s reuse those HI16s, and a HI16 left unresolved at a section end fails. On 30 real soft-float kernel modules, 24 contain several HI16s per LO16; all 54,972 placed relocations match. - External callees with float prototypes: the relocatable fixture now calls powf, whose prototype Ghidra's C library archive applies by name. Every variant must decompile `powf(1.5,2.5)` without FPU inputs, and the soft-float module variant must report that __rea_soft_float covered it (with the external step removed it decompiles as `powf(param_1,param_2)`). `-fno-builtin` keeps the call from being folded. Also updates the module variant's expected soft-float policy to `o32-soft-float-prototype-v1`. --------- Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
70 lines
1.7 KiB
TypeScript
70 lines
1.7 KiB
TypeScript
import type { MipsReadelfFacts, MipsFloatAbi } from "./mips-checks.mjs";
|
|
|
|
export interface MipsRelocatableSection {
|
|
index: number;
|
|
name: string;
|
|
type: string;
|
|
offset: number;
|
|
size: number;
|
|
allocated: boolean;
|
|
}
|
|
export interface MipsRelocatableSymbol {
|
|
index: number;
|
|
value: number;
|
|
type: string;
|
|
ndx: string;
|
|
name: string;
|
|
}
|
|
export interface MipsRelocation {
|
|
section: string;
|
|
offset: number;
|
|
type: number;
|
|
typeName: string;
|
|
symbolIndex: number;
|
|
}
|
|
export interface MipsRelocatableFacts {
|
|
sections: Map<number, MipsRelocatableSection>;
|
|
symbols: Map<number, MipsRelocatableSymbol>;
|
|
relocations: MipsRelocation[];
|
|
}
|
|
export interface MipsRelocationCoverage {
|
|
types: Record<string, number>;
|
|
shared_hi16: boolean;
|
|
multiple_hi16: boolean;
|
|
lo16_carry: boolean;
|
|
external_code: boolean;
|
|
external_data: boolean;
|
|
unplaced: number;
|
|
}
|
|
export function parseMipsRelocatableReadelf(text: string): MipsRelocatableFacts;
|
|
export function inspectMipsRelocatableReadelf(
|
|
path: string,
|
|
byteOrder: string,
|
|
floatAbi: MipsFloatAbi,
|
|
): Promise<
|
|
MipsReadelfFacts & MipsRelocatableFacts & { command: string; version: string }
|
|
>;
|
|
export function assertMipsRelocations(input: {
|
|
facts: MipsRelocatableFacts;
|
|
fileBytes: Buffer;
|
|
segments: readonly { name: string; start: string; end: string }[];
|
|
mappings: readonly {
|
|
block: string;
|
|
start: string;
|
|
length: number;
|
|
file_offset: number | null;
|
|
}[];
|
|
ghidraRelocations: readonly {
|
|
address: string;
|
|
type: number;
|
|
status: string;
|
|
}[];
|
|
words: Map<bigint, number>;
|
|
names: readonly {
|
|
address: string;
|
|
value: string;
|
|
symbol?: { external: boolean; type: string };
|
|
}[];
|
|
byteOrder: string;
|
|
}): MipsRelocationCoverage;
|