The jump addresses of call and jump instructions are now written to their own
and unique local variables. Before this, all jump instructions wrote to the
same local variable.
This was a problem for abstract interpretation: Because if multiple writes to
the same local var happen due to a previous TOP condition, the local variable
content is also TOP. If the jump target is TOP, the interpreter can't follow it
anymore.
Added tests for all the funny packet configurations with 0-2 jumps in it.
* Prefix Tree (Trie)
* Implemented complete RzTrie library, for prefix trees with all major APIs and full unit testing.
* Two unit tests: one for string and one for bitvector (to show usage). Almost 100% coverage
rz_debug_trace_ins_after() runs for every instruction while a debug session
records, and logged an error whenever the disassembler named a register
that is not in the debug register profile. x86-64 has no zmm or k entries,
so on a CPU where glibc selects the EVEX string routines every vector
instruction produced a line on stderr. In a `dc` under `dts+`, which single
-steps, that is one line per executed instruction; rz-test buffers all of
it, and the write cost alone can push a test over its timeout.
Demote it to a debug message and name the instruction address, so the same
information is available when it is wanted without being paid for on every
step.
rz_debug_trace_ins_before() dropped any memory write larger than 32 bytes
because rz_debug_trace_ins_after() read it into a fixed 32-byte stack
buffer. The write was then missing from the session, so stepping or
continuing back over it restored stale memory without saying so.
On x86-64 this fires on ordinary code: the glibc PLT and IFUNC resolvers
save vector state with XSAVE, which is a 576 byte write for the legacy
region plus header and more once AVX-512 state is enabled. Size the buffer
to the access instead, cap it at 4096 bytes, and warn with the address when
something exceeds that so the gap in the recording is visible.
% 2**4.5 asserted both the %.17g rendering and the exact bit pattern of
pow(2.0, 4.5). The exact value is 22.62741699796952078...; glibc and the
UCRT return the correctly rounded 0x4036a09e667f3bcd, while FreeBSD and
NetBSD return 0x4036a09e667f3bcc, one ULP low. msun's pow is documented as
under one ULP, not correctly rounded, so the test asserted bit-exact libm
behaviour for a transcendental and could not pass everywhere.
Filter the output down to the rounded line, which every implementation
within one ULP agrees on. The full float/scifmt/hex table stays covered by
the tests whose results are exactly representable.
rz_debug_native_map_get() splits a "<start>-<end>" region by copying the
tail into region2, but never terminated region at the '-'. That was
harmless while rz_num_get() stopped at the first character it could not
read; it no longer does, so the start token is now evaluated as a whole
expression:
0x7fc8124c4000-7fc81278d000 -> syntax error, 0
0x400000-0405000 -> 0x3df600, the end read as C octal
Every map then gets a wrong start address. linux_map_get() was fixed when
the parser changed; these three copies were missed. kfbsd.c reads the two
bounds as separate sscanf fields and is unaffected, as is the FreeBSD
map_get, which does not parse text at all.
Replace the hand-written parser in calc.c with a tree-sitter grammar
(subprojects/rizin-math-parser) and a typed evaluator. The old parser
could only ever produce a ut64 and folded anything it failed to read to
0, which left callers unable to tell a failed expression from one that
evaluated to zero.
Expressions now evaluate to an RzNumValue, a tagged union over ut64,
double, RzBitVector, arbitrary-precision integer and arbitrary-precision
decimal, carrying an RzNumError rather than signalling failure as 0.
Literals keep the width they were written with (5u8, 0xffu128, any width
from 1 to 65536), results that outgrow 64 bits promote to a big number on
their own, and a parse error, division by zero or unresolved identifier
reaches the caller.
rz_num_math() is deprecated. rz_num_math_ut64() keeps its exact behaviour
for callers that want a ut64, and rz_num_math_value() exposes the typed
result. rz_core_math() adds the RzCore-backed form used by the % command,
with rz_core_math_ut64() deprecated alongside it. rz-ax routes through the
typed API, so it prints values at full precision, reports errors on stderr
and exits non-zero. rz_il_lift_num() converts an expression to an
RzILOpPure, so a numeric argument can be lifted instead of pre-evaluated.
Legacy input still works: trailing base suffixes (101b, 35o, 212t), the
trailing-'h' hex form and the k/m/g scale suffixes are all accepted and
warn once, pointing at the 0b/0o/0t prefixes. doc/math.md documents the
language and doc/math-il-lift.md the lift; the grammar, the evaluator,
rz-ax and the % command are covered by unit and db tests.
* Update rz_config list variables to set variables
* Linking error fix
* Update rz_config_get_options in cautocmpl.c
* Update rz_config_get_options in core/tui/config.c
* Test fix
* Assertion error fix
To perform the effect in a delay slot, if the branch was not taken, the
IL, which is already lifted as part of the delay slot instruction, would
explicitly jump to itself again, to execute the effect as normal.
This would create erroneous loop edges in the cfg.
It is actually not necessary to perform this jmp since we already have
the lifted effect and can inline it.
While processing xrefs for marking them as data:
1. classify target using `xref_ref_kind` for data section too, previously it was only classified if target was in exec segment. Which caused false positive when the target was in non-exec section. Happens when the immediate value is small and it points in data section.
2. restrict data block from bleeding into other sections. Currently it correctly caps data block at next "detected" function (or next data) but when the function is not detected yet (like in stripped bins) and the area onward from data ref is empty, the data block bleeds into other sections specifically executable section. This should never happen.
Many IL ops such as add, sub, mul, ... share the same operand structure,
but previously in the RzILOpPure.op union there was only dedicated
members per exact opcode. So for code where multiple opcodes with
identical structure were handled, one had to either pick one of the
matching RzILOpPure.op members at random or add a large switch that was
technically unnecessary.
For such cases, we now make the structural identity explicit by
introducing shared operand structures such as RzILOpArgsBinopBV, which
can be used for all opcodes that match it.
Dedicated per-opcode typedefs and union members remain for when only a
single opcode is considered.
Flags are sorted into the name hashtable with their realnames as well.
Refcounting is used to prevent double-free and similar issues that would
be caused by this.
rz_reg_profile_to_cc() only emitted the first four argument registers
(A0-A3), so architectures that pass more arguments in registers -- the
C6000 EABI uses ten, and x86-64/riscv/ppc all declare more than four --
got a truncated convention. Walk the whole A0-A9 role range, stopping at
the first role the profile leaves undefined, and build the cc string with
RzStrBuf. Covered by a new test_reg unit test.
Co-authored-by agent: Claude/claude-opus-4-8
Co-authored-by: Anton Kochkov <anton.kochkov@gmail.com>