OpenRecomp — architecture-neutral static recompilation, MIPS overlap with rev.ng

Hi,

I’m the developer behind OpenRecomp, an open infrastructure project for deterministic static recompilation of legacy executables — extracting a versioned IR from a binary and translating it ahead of time into native code or WebAssembly, with execution and reproducibility as the primary goal rather than analysis alone.

The first proof of concept targets RV32I, and I’m preparing an NLnet Restack application to fund a second guest architecture, MIPS32, together with the validation and reproducibility work behind it.

rev.ng is one of the closer reference points I found while researching prior art. An architecture-neutral lifting framework that already covers MIPS is directly relevant to the IR, architecture-boundary, and host-contract choices I’m making.

I’m not looking for anything specific right now — I mainly wanted to say hello, flag that OpenRecomp exists, and ask whether MIPS lifting in rev.ng has exposed any control-flow, calling-convention, ABI, or compiler-pattern quirks that are particularly worth being aware of before I get deep into a MIPS32 adapter of my own.

Repo, if useful:

Thanks for the work you’ve put into rev.ng either way. It’s been a useful reference point for what an architecture-neutral binary translation pipeline can look like.

Fred

Hi, interesting project!

Some questions:

  1. Why some parts of the code are full of compact Python one-liners? o_O
  2. Is riscv32.py all there is about RISC-V? Seems very small.

As for MIPS, MIPS is weird, on Linux they don’t use the PLT but inline GOT loads, and they use a gp initialized at the beginning of the function, which is hard to prove it never changes (if you want to be architecture agnostic).

These are the main challenges that come to mind.

Thanks — these are good questions, and the MIPS point is especially useful.

The compact Python one-liners are mostly prototype/development debt rather than an intentional coding style. The early stages of OpenRecomp were built around small deterministic proof fixtures, so I prioritised getting the semantics, equivalence checks and fail-closed behaviour working first. I agree some of it is unnecessarily dense, and I’m planning to refactor those areas into more readable code without changing their behaviour.

adapters/riscv32.py is deliberately small, but it isn’t the whole RISC-V implementation. It is the architecture adapter/decoder for the currently supported RV32I subset. There is also the RV32I → architecture-neutral IR bridge, IR validation, execution/equivalence tooling and tests.

That said, I don’t want to oversell the current RISC-V support: it is still a bounded RV32I implementation. The current proof demonstrates equivalence for the synthetic E07 fixture and the instruction subset exercised by it, not arbitrary RISC-V binaries or complete RV32I support.

On MIPS: yes, I think you’ve identified one of the important next problems.

The current MIPS32 ELF work does not yet claim GOT/PLT, relocation or dynamic-linking support. In particular I don’t think an architecture-agnostic layer should simply assume that gp is constant for a function.

The direction I’m considering is to keep that reasoning in the MIPS frontend: treat gp as ordinary guest state, recognise ABI/compiler setup patterns where possible, track the reaching definition of gp, and only turn a GP-relative/GOT access into an architecture-neutral IR reference when that relationship can actually be proven. If it can’t be proven, the translator should fail closed rather than guess.

That way the core IR doesn’t need to know what a MIPS gp or GOT is — those remain architecture/ABI-specific facts which the frontend has to justify before normalization.

I’m still working through exactly how to handle this, so pointers to awkward real-world MIPS ELF examples or ABI cases would be very welcome. This is exactly the kind of feedback I’m hoping to get from publishing the project early.

recognise ABI/compiler setup patterns

Haram! :)

jk, at rev.ng we strive hard to make compiler-/ABI-specific assumptions to a minimum, but I guess in your context, recognizing patterns might make sense.