Skip to content

ffi: build without libffi complex number types - #503

Merged
jow- merged 4 commits into
masterfrom
fix/issue-502
Oct 23, 2026
Merged

jow- merged 4 commits into
masterfrom
fix/issue-502

Conversation

@jow-

@jow- jow- commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator

libffi only declares ffi_type_complex_float and ffi_type_complex_double
when the backend defines FFI_TARGET_HAS_COMPLEX_TYPE, which several
OpenWrt targets (powerpc, loongarch, riscv, ...) do not, so the module
failed to build:

lib/ffi/ffi.c: error: 'ffi_type_complex_double' undeclared

This branch detects the types at configure time and rejects complex
declarations at runtime where they are unavailable, and carries a few
related ffi fixes:

  • ffi: build without libffi complex number types
    Detect ffi complex types during configuration; a synthesized struct
    of two reals cannot be used instead since libffi passes it
    differently. Adds the predefined complex descriptors to the
    recognized set where libffi provides them.
  • ffi: guard against unexpected complex widths in uc_ctype_repr_complex()
    Unknown widths were misread as float pairs, whose conversions re-enter
    the function with the same width and exhausted the stack. Accept the
    three widths libffi provides and report unknown widths.
  • ffi: describe struct arguments to libffi properly
    Alignment was passed as the base-2 logarithm stored in the CType, and
    the member vector required by FFI_TYPE_STRUCT was never built, so
    ffi_prep_cif() crashed. Build the vector from the members and reject
    unions, which no descriptor can represent.
  • ffi: fix struct descriptors in closures and their lifetime
    Closure argument types were described through stale descriptor
    storage, and synthesized struct descriptors were leaked on every call
    and per closure since the FFI_TYPE_STRUCT comparison never matched.
    Track descriptors against libffi's predefined set and release them
    along their owner.

Adds testcases for structures passed by value, struct arguments in
closures, complex number conversion errors, and complex number types.

Fixes: #502

jow- added 4 commits October 23, 2026 08:46
A complex number is represented as a CT_ARRAY of two reals, so the width of the
value decides which pair of conversions converts it back. Any width other than
a pair of long doubles was taken for a pair of floats, which converted the
halves through conversions of the wrong type. Those conversions are themselves
complex, so they re-entered this function with the same width and exhausted the
stack.

Accept the three widths libffi provides complex types for and report an unknown
width instead. The other widths need a representation which does not exist yet,
so guessing one of the existing ones only turns a diagnostic into a recursion.

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Two defects kept structures from working as function arguments or return
values.

The predefined alignment was taken from the CType as is, but that field holds a
base 2 logarithm, so libffi saw 3 instead of 8 for a structure of doubles. It
now receives the plain byte value.

More seriously, no member vector was built at all. libffi needs one for a
FFI_TYPE_STRUCT: with a NULL vector initialize_aggregate() is unable to derive
size and alignment and ffi_prep_cif() ends up dereferencing the missing
members, which crashed the process. The vector is now built from the members of
the type, representing bitfields by their storage class and function members by
a pointer.

Because a union shares its storage, while a member vector describes consecutive
members whose sizes add up, no descriptor is able to pass a union the way the
ABI passes one. Such declarations are rejected now instead of silently passing
bogus values.

Structures passed by value, including nested ones and ones carrying bitfields,
now arrive at the callee as the ABI lays them out.

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Handing a closure's type descriptor storage to uc_ctype_to_ffi_type() as the
place to describe a type in kept structures from working as closure arguments.
The storage holds one entry per top level type, so a structure was described
through the entry of whichever type was converted into it before, which left it
with that type's size and alignment. ffi_prep_cif() then placed the argument
accordingly and crashed while reading the descriptor. The storage existed to
avoid allocating descriptors separately, which uc_ctype_to_ffi_type() does not
do anymore, so it is gone and the context keeps pointers only.

Describing a type properly made its lifetime worth getting right, which it was
not. Argument type descriptors were released by comparing them against
FFI_TYPE_STRUCT, a test that never matched as they refer to libffi's predefined
descriptors, so the descriptors synthesized for structures leaked on every
call. A closure leaks them too, as it is invoked long after the call which
created it.

Tell the two kinds apart by comparing a descriptor against the addresses of the
predefined ones, which form a closed set, and release everything else along its
owner: the descriptors of stack temporaries once the call returned, those of a
closure along the closure. Comparing against libffi's predefined descriptors is
what tells the two kinds apart, since inspecting a descriptor cannot: the kind
of a structure is FFI_TYPE_VOID until libffi has filled it in, and its size is
set from the type, so a structure of two 64 bit integers would look exactly
like ffi_type_longdouble and be released twice.

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
libffi declares ffi_type_complex_float and ffi_type_complex_double only when
the backend defines FFI_TARGET_HAS_COMPLEX_TYPE, which powerpc does not, so the
module failed to build for that target:

  lib/ffi/ffi.c: error: 'ffi_type_complex_double' undeclared

Detect the types while configuring and reject complex declarations at runtime
where they are unavailable. A complex value cannot be described as a struct of
two reals instead: libffi passes such a description differently than the
predefined type, so calls would end up passing bogus values.

Complex numbers are rarely declared, while a build failing on an unrelated
declaration is not, which makes the exception the better trade off.

The set of predefined descriptors grows by the complex ones where libffi
provides them, since a synthesized descriptor must never be mistaken for one.

Fixes: #502
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
@jow-
jow- merged commit 7b34723 into master Oct 23, 2026
3 checks passed
@jow-
jow- deleted the fix/issue-502 branch October 23, 2026 07:47
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.

error: 'ffi_type_complex_double' undeclared

1 participant