Skip to content

Fix IL operand handling mistakes in the C++, Rust and Python bindings - #8673

Open
bdash wants to merge 4 commits into
devfrom
test_il_operand_fixes
Open

bdash wants to merge 4 commits into
devfrom
test_il_operand_fixes

Conversation

@bdash

@bdash bdash commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

Each binding had its own mistakes in how it reads IL operands. Some instruction operands were read from the wrong slots, a few ops reported missing or wrong operands and variables, and Rust decoded ASSERT and FORCE_VER as Undef.

The Rust changes break source compatibility in a few places. rlc and rrc now take a carry. RegStackFreeReg is now an instruction rather than an expression. MLIL's CallOutput and NotYetImplemented variants are gone, and LowLevelILFlagWriteOp is no longer Copy.

See the individual commits for more details about the specific fixes.

bdash added 4 commits October 20, 2026 15:41
The operand tables behind GetOperands(), the IL operand classes and the
untyped instruction accessors read several ops' operands from the wrong
slots, or reported them under the wrong usage. MLIL's CopyTo also didn't
handle phis, and HLIL's expression ordering threw on WHILE_SSA and ignored
the operands of a few ops.

Now each op's operands are read from the right slots, CopyTo copies phis,
and expression ordering compares every operand.
… slice

Rust architectures were never asked for the flags of comparisons, float
operations or intrinsics. `LowLevelILFlagWriteOp` had no variants for them,
so the callback logged a warning and used core's default flag IL instead.
Each now has a variant. `Intrinsic` carries a parameter list, so the enum is
no longer `Copy`.

The callback also built its operand slice with `std::slice::from_raw_parts`,
which is undefined behavior if it is ever passed null. It now uses
`ffi::slice_from_raw_parts`, which returns an empty slice instead.
Rust's IL bindings read several ops' operands from the wrong slots, and
decoded ASSERT and FORCE_VER as Undef. The LLIL visitor also missed some
operands of intrinsics and the SSA calls. LLIL comparisons reported a
size of 0, unlike the C++ and Python bindings. The rlc and rrc lifters
took no carry, and the float conversion lifters lifted their source at
the wrong width.

Now each op decodes correctly, and ASSERT and FORCE_VER have their own
variants. MLIL's unused NotYetImplemented and CallOutput variants are
gone. Comparisons report their size like any other operation. rlc and
rrc take a carry, so msp430 now passes its carry flag.
Python's IL bindings reported no operands for ASSERT and FORCE_VER, and the
wrong variables as read or written for a handful of MLIL ops. ILOperations
named some operands after their slots rather than their properties. LLIL
also turned negative register-stack adjustments into -1, HLIL's ROR read a
carry it doesn't have, and a few HLIL builders stored their operands in the
wrong slots.

Now each op reports its operands and variables correctly, and the builders
store their operands in the right slots.
@bdash
bdash force-pushed the test_il_operand_fixes branch from 26b4495 to bcdadc1 Compare October 20, 2026 22:57

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant