diff --git a/deps/libffi/ChangeLog b/deps/libffi/ChangeLog index f2d63988dcf70a..0dde93e6adad9e 100644 --- a/deps/libffi/ChangeLog +++ b/deps/libffi/ChangeLog @@ -1,3 +1,953 @@ +commit 5c1c43091ed611fdea774374355eb938c73a9157 +Author: Anthony Green +Date: Fri Jul 10 09:50:53 2026 -0400 + + Prepare for 3.7.1 + + Bump version to 3.7.1 and libtool -version-info to 12:1:4 (revision + bump; no ABI change). See the README for the change list. + + Co-Authored-By: Claude Fable 5 + +commit aaeaafb04a8335bea93b2873ca9a2d01107737eb +Author: Anthony Green +Date: Fri Jul 10 09:24:36 2026 -0400 + + testsuite: Make closure_thiscall_fastcall_pop portable across compilers + + The i386 esp-balance shim faulted on Android i686 (clang, -O2): it read + the closure code pointer through an esp-relative memory operand after + manually moving esp, and it did not 16-byte align the stack for the + call, so the -O2-built closure body faulted on aligned SSE. + + Read every operand into a register while esp is still at its incoming + value, 16-byte align the stack at the call per the i386 psABI, avoid ebx + so it works under -fPIC, and return the delta via memory. Verified with + gcc and clang at -O0 and -O2: passes with the fix, still catches the + under-pop without it. + + Co-Authored-By: Claude Fable 5 + +commit ebe306becb167937ead4332290868e041d0c08a7 +Author: Anthony Green +Date: Fri Jul 10 08:54:39 2026 -0400 + + x86: Fix i386 THISCALL/FASTCALL closure stack-pop accounting + + For THISCALL and FASTCALL, ffi_closure_inner force-bumps narg_reg to 2 + when an argument is a 64-bit integer or a struct, so that following + integer arguments are placed on the stack (the Issue #434 rule). The + closure return path then computed the callee stack pop as + cif->bytes - narg_reg * 4, which discounts register slots that were + never used once narg_reg has been forced, under-popping the stack. + + For example FASTCALL void(uint64_t) has cif->bytes == 8 with the uint64 + placed on the stack, but the formula yields a pop of 0 instead of 8; a + callee-clean call site that does not re-sync ESP from a frame pointer is + then left with the argument bytes in place of its return address. + + Encode the pop as the number of bytes actually consumed from the + incoming stack (argp - stack, dir == 1 for these ABIs), which equals the + old formula in the non-forced cases and is correct in the forced ones. + + Adds closure_thiscall_fastcall_pop.c (i386/GNU), which invokes the + generated closure through a minimal callee-clean call site and checks + ESP is balanced; without this fix the imbalance is 8 (FASTCALL uint64) + or 4 (THISCALL this+uint64). + + Co-Authored-By: Claude Fable 5 + +commit a2ef91c79f4f193dd1e828f615c818fb6bcaff0d +Author: Anthony Green +Date: Fri Jul 10 08:20:39 2026 -0400 + + aarch64: Fix argument slab under-budget for large by-value structs + + On AArch64, composites larger than 16 bytes (that are not HFAs) are + passed by invisible reference. ffi_call copies each payload into the + argument slab from the top (next_struct_area, growing down) and, once + X0-X7 are exhausted, spills the by-ref pointer into the same slab from + the bottom (the NSAA, growing up). The generic prep_cif accounting in + cif->bytes only charges the payload copy, not the extra 8-byte pointer + slot, so with enough large structs the two regions collide: a later + payload copy overwrites an already-spilled pointer, and the callee then + receives a corrupt pointer for a by-value argument. + + Reserve an additional 8 bytes in ffi_prep_cif_machdep for each large + by-reference struct argument so the copy and spill regions can never + overlap. + + Adds many_large_structs.c, which passes sixteen 32-byte structs by + value; it crashes without this fix and passes with it. + + Co-Authored-By: Claude Fable 5 + +commit 25283b0d812b6729299a21a1c02433bc291174e9 +Merge: e87b59f5 665c5c00 +Author: Anthony Green +Date: Fri Jul 10 08:22:39 2026 -0400 + + Merge pull request #997 from libffi/windows-arm64-ci + + Add Windows ARM64 (MSVC) CI; fix latent msvcc.sh and testsuite bugs it exposed + +commit 665c5c00f2f492e199a1c9e01bda57a82f4440ef +Author: Anthony Green +Date: Fri Jul 10 07:46:24 2026 -0400 + + testsuite: Pass MSVC warning suppressions in go.exp + + Same latent issue as closure.exp: the Go closure tests ran with no + MSVC warning options, so ffitest.h's benign C4005 PRI-macro + redefinition warnings count as excess errors. Latent until the + msvcc.sh -E fix let the FFI_GO_CLOSURES feature probe succeed under + MSVC; the execution tests themselves pass on both x86 MSVC jobs. + Mirror the call.exp/closure.exp warning-suppression block. + + Co-Authored-By: Claude Fable 5 + +commit 2e897db821a43068e3851a414bd47b2b5592d403 +Author: Anthony Green +Date: Fri Jul 10 07:25:53 2026 -0400 + + testsuite: Skip closure_loc_fn0 memcmp check under trampoline tables + + Now that closure_loc_fn0.c actually executes, it fails on Apple + aarch64: with FFI_EXEC_TRAMPOLINE_TABLE, codeloc points at a + trampoline table entry rather than a copy of the closure, so the + memcmp sanity check is invalid there -- the same reason it is already + skipped for static trampolines. fficonfig.h (included by ffitest.h) + provides the macro. + + Co-Authored-By: Claude Fable 5 + +commit 15b9abc239a1a2f6964a4cfc92f8fb78b3475d37 +Author: Anthony Green +Date: Fri Jul 10 07:19:28 2026 -0400 + + ci: Add Windows ARM64 Visual C++ to the MSVC build matrix + + Build and test on GitHub's windows-11-arm runners. Cygwin and the + amd64_arm64 cross tools (cl, armasm64) are x64-hosted and run under + the runner's x64 emulation; the ARM64 binaries they produce execute + natively, so the full testsuite runs (784 passes, 0 failures). This + is the first CI coverage for the win64_armasm.S closure trampolines. + + The matrix entries grow runner/mflag/pkg/rcedit_arch parameters; the + existing x86 job names and release artifact names are unchanged. + Release tags now also publish ARM64 MSVC binaries + (libffi--arm64-msvc-binaries). + + Replaces the temporary winarm64.yml iteration workflow. + + Co-Authored-By: Claude Fable 5 + +commit e4435739c82106cf13336bf26da99a03281848a0 +Author: Anthony Green +Date: Fri Jul 10 06:58:17 2026 -0400 + + testsuite: Actually run closure_loc_fn0; fix msvcc.sh -S + + closure_loc_fn0.c is the only test in the suite without a dg-do + directive, so DejaGnu has been defaulting to its compile-only action + on every platform -- the ffi_closure_alloc/ffi_prep_closure_loc path + it exists to exercise was never executed. Add the missing + /* { dg-do run } */. + + The compile-only default also exposed msvcc.sh's broken -S handling: + it mapped -S to -FAs alone, which emits a listing but still runs the + link step, failing with unresolved externals since compile-only + invocations pass no libraries. Map -S to a true compile-to-assembly: + -c -FAs with the listing written to the -o target. + + Co-Authored-By: Claude Fable 5 + +commit bf2fa49b6dbe68dac021c02ee3deddb343ec3541 +Author: Anthony Green +Date: Fri Jul 10 06:36:26 2026 -0400 + + testsuite: Pass MSVC warning suppressions to closures C tests + + closure.exp defines additional_options with the same -wd warning + suppressions call.exp uses, but only passed them to the C++ tests; + the C tests ran with "". Under MSVC the benign C4005/C4305/C4477 + warnings then count as excess errors and fail every C test's compile + check. This was latent until the previous commit made the + FFI_CLOSURES feature probe work under msvcc.sh, allowing the closures + suite to run on MSVC at all. + + Co-Authored-By: Claude Fable 5 + +commit c2ace700754b6e63f07efdec39fc8c09ed960d63 +Author: Anthony Green +Date: Fri Jul 10 06:11:00 2026 -0400 + + msvcc.sh: Handle -E (preprocess) so DejaGnu feature tests work + + The testsuite's libffi_feature_test probes compile a snippet with + "-E -o " and treat any compiler output as failure. msvcc.sh + had no -E handling: cl's preprocessed text went to stdout, where the + awk diagnostics filter passed through every line containing the + string "warning" -- including ffi.h's own #pragma warning lines -- + and cl printed the source file name to stderr. The probes therefore + always failed under msvcc.sh, silently marking the entire closures + suite (99 tests) and the bhaible callback tests (80) UNSUPPORTED on + every MSVC CI job. + + Recognize -E, write the preprocessed output to the -o target, and + swallow cl's stderr banner so feature probes see clean output. + + Co-Authored-By: Claude Fable 5 + +commit e994c8a22d6bc480cd13c9daa2abaedf42627f88 +Author: Anthony Green +Date: Fri Jul 10 05:49:48 2026 -0400 + + ci: Add Windows ARM64 Visual C++ job (iteration branch) + + Build and test libffi on GitHub's windows-11-arm runners using + msvcc.sh -marm64 (cl + armasm64 via the amd64_arm64 cross tools, + running under x64 emulation; the resulting ARM64 binaries execute + natively). msvcc.sh already understands -marm64. + + This is a temporary standalone workflow scoped to the + windows-arm64-ci branch so the job can be iterated on without + triggering the full CI matrix; it will be folded into build.yml + once green. + + Co-Authored-By: Claude Fable 5 + +commit e87b59f53f3879cb254a4c331fd0d2c221d6ea73 +Author: Anthony Green +Date: Fri Jul 10 05:40:57 2026 -0400 + + aarch64: Fix clang-cl link failure for HFA helper functions + + clang-cl defines both __clang__ and _MSC_VER. Since the build system + detects clang before MSVC, it sets MSVC=0 and builds sysv.S rather + than win64_armasm.S. But ffi.c gated its C implementations of + extend_hfa_type and compress_hfa_type on !defined(_MSC_VER), so under + clang-cl neither implementation was built and linking failed. + + Gate on defined(_MSC_VER) && !defined(__clang__) instead: clang-cl + supports GCC-style extended inline asm, so the C implementations work + there. Also replace ssize_t (POSIX-only, absent on Windows) with + ptrdiff_t in extend_hfa_type now that it compiles for MSVC targets. + + Fixes #996 + + Co-Authored-By: Claude Fable 5 + +commit cacaf519159668bce9fcbab64082bc85c277e89d +Author: Anthony Green +Date: Thu Jul 9 21:34:47 2026 -0400 + + x86-64: build generic ffi_call_plan fallback on Windows x86-64 + + The ffi_call_plan_* API has two providers: the accelerated x86-64 SysV + implementation in src/x86/ffi64.c, and the portable fallback in + src/prep_cif.c that just wraps ffi_call. ffi64.c is not compiled for + X86_WIN64 (that target builds ffiw64.c/win64.S instead), but clang-cl + and MSYS/mingw both define __x86_64__, so prep_cif.c's guard suppressed + the fallback too -- leaving ffi_call_plan_alloc/invoke/free undefined at + link time on those toolchains. MSVC cl escaped this only because it does + not define __x86_64__. + + Suppress the generic fallback only when ffi64.c actually provides the + functions, i.e. add && !defined(X86_WIN64) to the guard. X86_WIN64 is a + command-line define visible to the generic sources (cf. src/closures.c). + + Fixes #995. + + Co-Authored-By: Claude Opus 4.8 (1M context) + +commit c2c2e538c7c5403bd4edc3e77220bbe25a3ad47f +Merge: 132fb82f 7c3144c8 +Author: Anthony Green +Date: Thu Jul 9 09:10:49 2026 -0400 + + Merge pull request #994 from libffi/fix-993-aarch64-darwin-int128 + + aarch64: Don't split an int128 between x7 and the stack on Darwin + +commit 7c3144c80d8077b745588ee64bffd8504c44f5c9 +Author: Anthony Green +Date: Wed Jul 8 21:23:31 2026 -0400 + + aarch64: Don't split an int128 between x7 and the stack on Darwin + + The Apple arm64 ABI does not round NGRN up to an even number for + 128-bit integer arguments, but like AAPCS64 it still requires the + value to fit entirely in registers: if only x7 remains, the whole + value goes on the stack. allocate_int128_to_reg_or_stack only + checked ngrn < 8, so with ngrn == 7 it wrote the low half to x7 + and the high half past the end of the register array, while the + callee reads the argument from the stack. + + This fixes the libffi.call/i128-1.c failure at iteration 7 (seven + int args followed by an __int128) on aarch64-darwin, for both the + call and closure paths. + + Fixes #993 + + Co-Authored-By: Claude Fable 5 + +commit 132fb82ff4b43c9b8eaeeb533f38c95ae925131c +Author: Anthony Green +Date: Wed Jul 8 07:40:34 2026 -0400 + + Update libtool version to 12:0:4 for 3.7.0 + + 3.7.0 adds three new public interfaces (ffi_call_plan_alloc, + ffi_call_plan_invoke, ffi_call_plan_free) additively, with no + existing interface removed or changed. Per the libtool rules: + increment current, reset revision, increment age (11:1:3 -> 12:0:4). + This keeps the change ABI backward-compatible; the soname stays + libffi.so.8 (current - age = 8). + + Co-Authored-By: Claude Opus 4.8 (1M context) + +commit 0ad8fd46c0a32cbbf18260c72cc60679da66c89f +Author: Anthony Green +Date: Wed Jul 8 06:43:03 2026 -0400 + + Prepare for 3.7.0 + +commit c9275302baf0d109ec474972d4679a6afe8fe64d +Author: Anthony Green +Date: Wed Jul 8 06:29:29 2026 -0400 + + Bump version to 3.7.0 + + Set AC_INIT to 3.7.0 and update the manual's edition/version/date + stamps to match. + + Co-Authored-By: Claude Fable 5 + +commit 4f7ef24565eb60e8a093bfeba0bb77d5c576c311 +Author: Anthony Green +Date: Wed Jul 8 06:28:30 2026 -0400 + + Update copyright year + + Bump the LICENSE year range to 2026. + + Co-Authored-By: Claude Fable 5 + +commit 5ab7b328c3adde14105b1f36ae30adc9cdefae0a +Author: Anthony Green +Date: Wed Jul 8 06:24:26 2026 -0400 + + Add Anthony Green copyright line for 2026 authorship + + Attribute this year's work in the wasm and PA backends and the darwin + PowerPC closure code: + - wasm/ffi.c: widen narrow/unboxed returns to ffi_arg; NULL rvalue. + - pa/ffi.c: bump year (5-8 byte struct slot-sizing fix + avalue copy). + - powerpc/ffi_darwin.c: PPC_LD_* jump-table indexes for the aix/darwin + closure build fix. + + Co-Authored-By: Claude Fable 5 + +commit 5e334c6aa70b937b5c0d30ba10a373e34d7526b6 +Author: Anthony Green +Date: Wed Jul 8 06:17:58 2026 -0400 + + x86-64: add copyright line for the 2026 unix64.S plan trampolines + + The fast-path plan trampoline (ffi_plan_fast_call) and the pure-GP64 + direct thunks (ffi_plan_gp0..6) were added to unix64.S this year, so + add an Anthony Green copyright line alongside the original authors'. + + Co-Authored-By: Claude Fable 5 + +commit 70106b98d0a333a13b509b4bdc554018785be50c +Author: Anthony Green +Date: Wed Jul 8 06:15:53 2026 -0400 + + Update copyright year to 2026 + + Add 2026 to the copyright line on the source files, headers and the + manual touched during this year's development. Following existing + practice, only the maintainer's own copyright line is bumped (the + Anthony Green line, plus the Red Hat line in types.c); the years on + third-party-held backends are left as their original authors set them. + + Co-Authored-By: Claude Fable 5 + +commit 621874d6726007a6470b9c0c90b5a27c3c3ed932 +Author: Anthony Green +Date: Wed Jul 8 05:43:18 2026 -0400 + + configure: derive FFI_VERSION_STRING/NUMBER from AC_INIT + + The 3.6.0 release bumped AC_INIT but left FFI_VERSION_STRING and + FFI_VERSION_NUMBER at 3.5.2/30502, so the released headers report the + wrong version. Compute both from AC_PACKAGE_VERSION instead of + duplicating the version by hand: the string is the package version + verbatim, and the number encodes X.Y.Z as X*10000 + Y*100 + Z with any + non-numeric suffix (e.g. -rc0) ignored. + + Co-Authored-By: Claude Fable 5 + +commit b8c60b06597cc77cbe66bd208083acc20251fae0 +Author: Anthony Green +Date: Tue Jul 7 12:57:06 2026 -0400 + + README: note the ffi_call fixes in the 3.7.0 history + + Co-Authored-By: Claude Fable 5 + +commit ed5da041796cd6e38d2f30b948eadf63cf96139a +Author: Anthony Green +Date: Tue Jul 7 12:30:56 2026 -0400 + + wasm: don't widen unboxed small-struct returns + + The previous commit widened narrow integral returns to ffi_arg, but + unbox_small_structs had already collapsed single-member struct returns + into those same scalar type ids, and a struct return is stored at its + natural size -- the caller's buffer is exactly the struct. This broke + cls_1_1byte and the bhaible small-struct return tests. + + Remember whether the original return type was a struct and widen only + genuine scalar returns. + + Co-Authored-By: Claude Fable 5 + +commit 59a9e68e59205da9c4db854dd1f1bce4e8b58dad +Author: Anthony Green +Date: Tue Jul 7 12:02:20 2026 -0400 + + wasm: widen narrow integral returns to ffi_arg + + The return store wrote only the natural width for integer returns + narrower than a register (one byte for U8/S8, two for U16/S16, four on + wasm64 for 32-bit types), leaving the rest of the caller's ffi_arg + buffer as stack garbage. The documented API widens such returns to a + full ffi_arg, and every native port stores the full register width. + This is what plan_mixed.c caught: two identical calls disagreed in the + garbage bytes of their ffi_arg results. + + Mask or sign-extend the JS result explicitly and store a full ffi_arg + (32 bits on wasm32, 64 on wasm64). + + Co-Authored-By: Claude Fable 5 + +commit 6954bd1715b8a5895adfad7c2196d4eb41094dc7 +Author: Anthony Green +Date: Tue Jul 7 11:30:55 2026 -0400 + + testsuite: use ffi_arg for the integral return in plan.c + + The struct-argument block stored an ffi_type_slong return into a plain + long. Integral returns narrower than a register are widened to + ffi_arg, so on LLP64 targets libffi writes 8 bytes into the 4-byte + variable and clobbers its neighbor; this is what broke plan.c on MSVC + Win64 while mingw survived by stack-layout luck. + + Co-Authored-By: Claude Fable 5 + +commit 822d2e555fd97271c7c0ba8b6325cf4b541e3e9c +Author: Anthony Green +Date: Tue Jul 7 11:30:55 2026 -0400 + + Allow a NULL rvalue in ffi_call; handle it in the wasm port + + Most ports already quietly supported calling with rvalue == NULL by + substituting scratch space for in-memory returns, and the new plan + tests rely on it, but the manual never promised it and the wasm port + passed NULL straight to the callee as the return-by-argument pointer, + writing the result through address zero. + + Document NULL rvalue as supported for any return type, and in the wasm + port allocate stack scratch for return-by-argument callees and skip the + store-back for scalar returns when rvalue is NULL. + + Co-Authored-By: Claude Fable 5 + +commit 110156e1cceab76b4440ced9d95b5abaa84f30f7 +Author: Anthony Green +Date: Tue Jul 7 11:30:55 2026 -0400 + + ffi_call: never modify the caller's avalue array + + Eight ports made large struct arguments pass-by-value by alloca'ing a + copy inside ffi_call and writing the copy's address back into the + caller's avalue[]. Those pointers dangle as soon as ffi_call returns, + so any caller that legally reuses its avalue array for a second call + reads dead stack. The new ffi_call_plan equivalence tests do exactly + that and exposed it as plan_struct.c failures on hppa, MSVC Win64 and + macOS x86_64 under Rosetta; other targets passed only because the dead + copy usually survives between back-to-back calls. + + Clone the pointer array once, on the first argument that needs a copy, + and redirect the clone instead. Document that ffi_call leaves the + caller's vector untouched. + + Co-Authored-By: Claude Fable 5 + +commit e3dae4657710a9ddd4eb4bdbefbcc73722aad9fe +Author: Anthony Green +Date: Tue Jul 7 09:18:31 2026 -0400 + + testsuite: add ffi_call_plan equivalence tests; fix EXTRA_DIST gaps + + Add plan.c, plan_mixed.c, plan_spill.c, plan_struct.c and plan_var.c. + Each builds a reusable call plan (ffi_call_plan_alloc/invoke/free) and + checks it against ffi_call across the x86-64 argument-placement matrix: + the pure-GP64 direct thunks, the mixed GP+SSE fast path, stack-spilled + arguments, struct returns (in-memory including a NULL rvalue, and the + register pair), a struct-by-value argument that falls back to ffi_call, + and variadic calls. On targets without the accelerated backend the + generic plan simply wraps ffi_call, so the tests still pass there. + + While adding these to EXTRA_DIST, register four existing tests that were + never listed (i128-1.c, large_struct_by_value.c, many_small_structs.c + and complex_i128.c). They ran via the .exp globs but were missing from + distribution tarballs. + + Co-Authored-By: Claude Fable 5 + +commit a40ab3a7e85f8c9b193b8a442297bb20de2b4293 +Author: Anthony Green +Date: Tue Jul 7 08:19:33 2026 -0400 + + README: update 3.7.0 history entry + + Co-Authored-By: Claude Fable 5 + +commit 50baa0458db6b2d617e0d661292bbf12354b22ce +Author: Anthony Green +Date: Tue Jul 7 08:10:29 2026 -0400 + + x86_64: size closure argument buffer to match copy loop + + ffi_closure_unix64_inner allocated a fixed 16 bytes when gathering a + register-passed argument's eightbytes, but the copy loop writes n * 8 + bytes and examine_argument can classify up to MAX_CLASSES (4) + eightbytes for vector-classified types. Allocate n * 8 bytes so the + buffer always matches the loop bound. + + Co-Authored-By: Claude Fable 5 + +commit ff30d37615f82ef7bf1130b34528e01e1479b63d +Author: Anthony Green +Date: Tue Jul 7 08:10:29 2026 -0400 + + tramp.c: bound sscanf path field when parsing /proc/self/maps + + The %s conversion into file[PATH_MAX] was unbounded while the input + line buffer is PATH_MAX+100 bytes, so a mapping with a very long + pathname could overflow the destination. Give the conversion an + explicit PATH_MAX field width and size the buffer PATH_MAX+1 to + accommodate the terminating NUL. + + Co-Authored-By: Claude Fable 5 + +commit ed1844e6b50d0498d2b88d1b3ef31cc39b6cf117 +Author: Anthony Green +Date: Tue Jul 7 08:10:29 2026 -0400 + + .gitignore: ignore local build and scan artifact directories + + Co-Authored-By: Claude Fable 5 + +commit 854df63dbbda802985facbfab7a0e7c4169aefd1 +Merge: 2a595a18 731ec08f +Author: Anthony Green +Date: Tue Jul 7 08:06:25 2026 -0400 + + Merge pull request #991 from rorth/freebsd-libffi-note-gnu-stack + + Include .note.GNU-stack on FreeBSD/x86 + +commit 731ec08f3c3bbec883d823a71f1b0f86731cf312 +Author: Rainer Orth +Date: Tue Jul 7 11:14:09 2026 +0200 + + Fix typo. + +commit 19cc1424e924767c7ca43f9e4d4c27fbae27d1d9 +Author: Rainer Orth +Date: Tue Jul 7 10:27:55 2026 +0200 + + Include .note.GNU-stack on FreeBSD/x86 + + When building libffi on FreeBSD/x86 with --disable-shared as is done inside + the GCC tree, all tests FAIL when using GNU ld: + + gld-2.46: warning: win64.o: missing .note.GNU-stack section implies executable stack + gld-2.46: NOTE: This behaviour is deprecated and will be removed in a future version of the linker + + The emission of .note.GNU-stack in src/x86/*.S is currently guarded by + __ELF__ && __linux__, but FreeBSD/x86 uses that NOTE section, too. This + doesn't happen with libffi.so which lacks that NOTE. + + Therefore this patch extends the emission accordingly. + + Tested on amd64-pc-freebsd15.1 with and without --disable-shared. + +commit 2a595a183be146974bdf55716969e7a3e860f112 +Merge: c93f9428 f7b36640 +Author: Anthony Green +Date: Thu Jul 2 20:03:30 2026 -0400 + + Merge pull request #989 from rvandermeulen/dlmalloc-msvc-lock-cast + + dlmalloc: cast Win32 spin-lock word to LONG volatile* in CAS_LOCK/CLEAR_LOCK + +commit f7b36640544a59813ee372ed1a763affe7a67562 +Author: Ryan VanderMeulen +Date: Thu Jul 2 19:32:00 2026 -0400 + + dlmalloc: cast Win32 spin-lock word to LONG volatile* in CAS_LOCK/CLEAR_LOCK + + MLOCK_T is int, but on the Win32 MSC path CAS_LOCK/CLEAR_LOCK map to + _InterlockedExchange(LONG volatile *, LONG), so the int* lock word is + passed to a LONG volatile * parameter. clang 22 promotes + -Wincompatible-pointer-types to an error by default, which breaks the + clang-cl build (32- and 64-bit) with: + + error: incompatible pointer types passing 'int *' to parameter of type + 'volatile long *' [-Wincompatible-pointer-types] + + Cast to (LONG volatile *), matching what the ffi_spin_peek macro just + below already does in its _MSC_VER branch. LONG and int are both 32-bit + on Windows, so this is a type-correctness fix with no behavioral change. + +commit c93f9428d17cde4eb35517b58feeae6fb43aba5b +Author: Anthony Green +Date: Mon Jun 22 17:33:33 2026 -0400 + + testsuite: link threads tests against libpthread on NetBSD + + The libffi.threads/tsan.c test calls pthread_create but the harness + only appended -lpthread for OpenBSD, FreeBSD, and some Linux targets. + On NetBSD this caused an undefined reference to pthread_create. + + Fixes #988 + + Co-Authored-By: Claude Opus 4.8 (1M context) + +commit 9ca53a19833dabaf80b73763638ab988bdbc42cc +Author: Anthony Green +Date: Sun Jun 21 18:42:37 2026 -0400 + + powerpc: fix aix/darwin closure build errors + + unbuildable: the PPC_LD_* jump-table indexes (defined in ffi_powerpc.h, + which this file cannot include) were missing, and the closure helper + functions still declared an ffi_type* return type inconsistent with the + PPC_LD_* constants they now return. + + Define the PPC_LD_* constants locally and change the closure helper + return types to int. + + Fixes #987 + + Co-Authored-By: Claude Opus 4.8 (1M context) + +commit 807a93115f927229f6e7cf5675384e8dc2bf76d5 +Author: Anthony Green +Date: Sun Jun 21 18:31:12 2026 -0400 + + doc: mark the 3.7.0 release date as TBD + + Co-Authored-By: Claude Opus 4.8 (1M context) + +commit c876d4663183c40edbe5218603a26290900e7794 +Author: Anthony Green +Date: Sun Jun 21 18:28:12 2026 -0400 + + doc: add 3.7.0 history entry + + Record the next release (3.7.0, a minor bump for the new reusable call-plan API + on master) and the two PA-RISC fixes merged in #986. + + Co-Authored-By: Claude Opus 4.8 (1M context) + +commit c3e054afebb2d9121ff4d12a56a803eecc31cc41 +Merge: 3cc6beb7 1f04d458 +Author: Anthony Green +Date: Sun Jun 21 18:13:59 2026 -0400 + + Merge pull request #986 from libffi/ci-enable-parisc-regression + + Add PA32/ARCompact struct-by-value regression tests; enable hppa CI + +commit 1f04d458126bd79c58196073477515c88de8163d +Author: Anthony Green +Date: Sun Jun 21 17:37:26 2026 -0400 + + pa: fix stack sizing for 5-8 byte structs passed by value (FFI_PA32) + + ffi_size_stack_pa32 reserved a single stack slot for every struct argument, + but ffi_prep_args_pa32 passes a 5-8 byte struct inline in two even-aligned + slots (like a 64-bit value). With enough such arguments the marshaller's slot + count outgrew cif->bytes and it wrote past the allocated frame -- below sp and + over ffi_call_pa32's saved return pointer -- corrupting the call. + + Make the sizing mirror the marshalling exactly: 1-4 byte structs take one slot, + 5-8 byte structs take "2 + (z & 1)" slots (z stays offset from the marshaller's + slot by the odd FIRST_ARG_SLOT, so the parity matches), and larger structs are + passed by pointer in one slot. Split the PA_HPUX long double case out since it + is passed by pointer. + + Fixes the failure exercised by testsuite/libffi.call/many_small_structs.c. + + Co-Authored-By: Claude Opus 4.8 (1M context) + +commit cf45dfc368ea7373fdd946603845782dbcc1549a +Author: Anthony Green +Date: Sun Jun 21 16:57:56 2026 -0400 + + testsuite: make many_small_structs actually trigger the PA32 overflow + + With only 16 structs the PA-RISC slot under-count overflows ~32 bytes below + sp, landing in the 64-byte register save area where it clobbers nothing live, + so the call still returned correctly and the test passed under qemu. + + Raise the count to 40 so the marshaller's writes sweep past the save area and + over ffi_call_pa32's saved return pointer (sp-20), corrupting the return path. + On a correct backend the arguments still marshal within the allocated frame and + the call returns the expected sums; the test now fails only on an unfixed + FFI_PA32. + + Co-Authored-By: Claude Opus 4.8 (1M context) + +commit d55de2637abd9518d550d18a3e1d89a37bd32f47 +Author: Anthony Green +Date: Sun Jun 21 16:39:30 2026 -0400 + + pa: fix build broken by the int128 generic types + + FFI_TYPE_UINT128/FFI_TYPE_SINT128 were added after FFI_TYPE_COMPLEX, moving + FFI_TYPE_LAST to FFI_TYPE_SINT128. The PA-RISC tripwire still pinned + FFI_PA_TYPE_LAST to FFI_TYPE_COMPLEX, so "#error You likely have broken jump + tables" fired and PA-RISC stopped building once int128 support landed. + + ffi_prep_cif_machdep maps every return type it does not handle explicitly -- + including FFI_TYPE_COMPLEX and the 128-bit integer types -- to FFI_TYPE_INT via + its default case, so cif->flags never exceeds FFI_TYPE_COMPLEX and the linux.S / + hpux32.S return-value jump tables remain sufficient. Bump FFI_PA_TYPE_LAST to + FFI_TYPE_SINT128 to clear the tripwire, with a comment recording why no table + entries were needed. + + Co-Authored-By: Claude Opus 4.8 (1M context) + +commit d7177f2db574c962b4366b1948b7f7e1aaf896c2 +Author: Anthony Green +Date: Sun Jun 21 16:26:53 2026 -0400 + + ci: run the testsuite on PA-RISC 32-bit (hppa) under qemu-user + + Add an hppa-linux-gnu entry to the build-cross-qemu matrix so the testsuite is + cross-compiled with the Debian gcc-hppa-linux-gnu toolchain and run under + qemu-user (binfmt). hppa-linux uses FFI_PA32, so this exercises the + ffi_prep_args_pa32 / ffi_size_stack_pa32 marshalling path and will surface the + many_small_structs.c regression until the backend is fixed. + + Also drop the stale g++-5-hppa-linux-gnu pin from install.sh; the toolchain and + qemu-user-static now come from the CROSS_QEMU block. + + Co-Authored-By: Claude Opus 4.8 (1M context) + +commit a384cba94f3935ac54a38f7c6489b6fbe0488c8c +Author: Anthony Green +Date: Sun Jun 21 16:26:21 2026 -0400 + + testsuite: add struct-by-value regression tests for PA32 and ARCompact + + Two ffi_call tests covering argument-marshalling defects found by a security + review of the backends: + + * many_small_structs.c -- passes sixteen 8-byte structs by value. On + PA-RISC 32-bit (FFI_PA32), ffi_size_stack_pa32 reserves one stack slot per + struct while ffi_prep_args_pa32 consumes two slots for a 5-8 byte struct + passed inline, so enough such args write past the asm-allocated frame. + + * large_struct_by_value.c -- passes one 64-byte struct by value. On + ARCompact (FFI_ARCOMPACT), ffi_call_int sizes the stack arg area at two + words per argument, but a by-value struct is marshalled one word at a time + and words beyond the eight argument registers are written to that + under-sized area without bound. + + Both are written as ordinary correctness tests (valid CIFs, asserts on the + expected sums); they pass on unaffected targets and fail on PA32/ARCompact + until the backends are fixed. + + Co-Authored-By: Claude Opus 4.8 (1M context) + +commit 3cc6beb7d404e12d3458461d76557b79795816ff +Author: Anthony Green +Date: Sun Jun 21 09:47:10 2026 -0400 + + x86-64: replace the transparent plan cache with an explicit ffi_call_plan API + + The transparent plan cache sped up ffi_call and closures by caching a + precomputed argument-placement plan per signature, but it charged every + call a cache lookup and content fingerprint, and it slightly regressed + signatures with no fast path (struct-by-value) that paid the key + computation only to find there was nothing to do. + + Replace it with an opt-in API. ffi_call_plan_alloc builds a plan from a + prepared cif, ffi_call_plan_invoke applies it with no per-call lookup, + and ffi_call_plan_free releases it. The plan is opaque and caller-owned, + immutable once built (so it may be shared across threads), and falls back + to ffi_call for signatures with no fast path. ffi_call and the closure + path return to their original behaviour, so callers that don't adopt the + API pay nothing. + + The x86-64 backend provides an accelerated plan; every other target gets + a portable fallback in prep_cif.c that wraps ffi_call, so the symbols are + defined on all platforms. The per-thread cache, content fingerprint, + thread-exit cleanup and closure demarshal layout are removed. + + Co-Authored-By: Claude Opus 4.8 (1M context) + +commit 0c8f175eb650bb204b679996696b3e7706505d8f +Author: Anthony Green +Date: Sun Jun 21 08:18:14 2026 -0400 + + x86-64: trim the plan-cache fingerprint to the arg_types contents + + abi, nargs and rtype are stored in the slot and compared exactly on every + hit, so folding them into the fingerprint is redundant work on the hot + path. Hashing only the arg_types element pointers keeps the same collision + behaviour and the same protection against a reused arg_types address, while + shortening the per-call hash and speeding up the all-pointer thunk path. + + Co-Authored-By: Claude Opus 4.8 (1M context) + +commit 7b8c3283351298c55d09f580414ab937f358156c +Merge: d4995b5b f1e21329 +Author: Anthony Green +Date: Sat Jun 20 19:48:22 2026 -0400 + + Merge pull request #985 from libffi/plan-cache-fingerprint + + x86-64: content-based plan-cache key and thread-exit cleanup + +commit f1e2132940880128e26b885ee956e76fe3cad2d8 +Author: Anthony Green +Date: Sat Jun 20 19:46:50 2026 -0400 + + x86-64: content-based plan-cache key and thread-exit cleanup + + Follow-up to the plan cache (PR #984), addressing two issues. + + Content key: the cache keyed slots on the abi/nargs/rtype plus the arg_types + *array pointer*. If a cif's arg_types array was freed and the address later + reused for a different signature with the same abi/nargs/rtype, the keys + collided and a stale plan could be returned. Key on a 64-bit fingerprint that + folds in the element ffi_type pointers, so distinct argument types always get + distinct keys. Lookup is now O(nargs) cheap pointer reads -- still far less + than re-classifying the call. + + Thread-exit cleanup: the per-thread cache leaked its malloc'd plans when a + thread exited. Register a pthread key whose destructor frees the thread's + plans. pthread is referenced via weak symbols, so a program that never links + pthread simply skips the registration (the cache is already bounded per thread). + + Co-Authored-By: Claude Opus 4.8 (1M context) + +commit d4995b5bff6e5ecefb46eda218c40717f895baaf +Merge: 918ca8ed de3826a8 +Author: Anthony Green +Date: Sat Jun 20 18:43:14 2026 -0400 + + Merge pull request #984 from libffi/fast-libffi-plan + + x86-64: transparent plan cache for ffi_call and closures + +commit de3826a8063fde113ee8d1e455d459038d5814cc +Author: Anthony Green +Date: Sat Jun 20 18:14:08 2026 -0400 + + x86-64: limit the plan cache to the SysV ABI + + The Win64/EFI64 plan path failed an MSVC execution test (libffi.call/float3) + that can't be reproduced or validated without a Windows toolchain. Revert + ffiw64.c to the proven upstream implementation and build plans only for + FFI_UNIX64, leaving Win64/EFI64/GNUW64 on the existing path. The SysV fast + path -- the bulk of the speedup -- is unaffected. + + Co-Authored-By: Claude Opus 4.8 (1M context) + +commit fdbf8deaf297b308731385f91868ea8b5e7dbd21 +Author: Anthony Green +Date: Sat Jun 20 17:59:54 2026 -0400 + + x86-64: fix Windows build of the plan cache + + Native Windows targets build only ffiw64.c + win64.S for the x86 backend; + ffi64.c (which instantiates the plan-cache storage and ffi_build_plan_arch) + is not compiled, so ffiw64.c's ffi_plan_get left ffi_plan_miss / ffi_plan_cache + unresolved at link time. Instantiate the cache in ffiw64.c when X86_WIN64 is + defined (i.e. when ffi64.c is absent); on Unix x86-64 ffi64.c still does it and + X86_WIN64 is undefined, so there is no duplicate definition. + + Also use a portable thread-local qualifier (FFI_TLS: __declspec(thread) on + MSVC, __thread elsewhere). + + Co-Authored-By: Claude Opus 4.8 (1M context) + +commit 872365e143c521b4a84f712aa60f4e638a8b8053 +Author: Anthony Green +Date: Sat Jun 20 17:25:17 2026 -0400 + + x86-64: reword plan-cache comments to be self-contained + + Drop the in-development "Mechanism A" label and the stale references to the + earlier opt-in arena/slab storage, so the comments describe the cache as it + now stands. No functional change. + + Co-Authored-By: Claude Opus 4.8 (1M context) + +commit c4c9750c37868f26d1ce914592ca826e4ffc0b35 +Author: Anthony Green +Date: Sat Jun 20 13:10:18 2026 -0400 + + x86-64: transparent plan cache for ffi_call and closures + + Precompute each signature's argument placement once and cache it, keyed on + (abi, nargs, rtype, arg_types) in a per-thread direct-mapped cache, so ffi_call + and closures skip the per-call ABI classification. No API or ABI change -- + every caller benefits transparently on a relink. + + - Cache: src/plan-cache.h (inline O(1) probe) + src/plan-cache-impl.h + (per-thread storage, evict-and-free, FFI_PLAN_NONE for non-plan-able sigs). + - SysV (ffi64.c, unix64.S): pure-GP64 direct thunks (avalue -> arg regs), a + lean trampoline for register-only calls, and a plan executor reusing + ffi_call_unix64; closures use a precomputed demarshal layout. CFI on the + thunks/trampoline so exceptions unwind through them. + - Win64/EFI64 (ffiw64.c): plan executor + demarshal. + - W^X preserved: no runtime codegen; thunks shipped in .text. + + Scalar/pointer/int128/float/double args are accelerated; struct/complex/x87 + args fall back to the existing path. Pointer-heavy calls ~5x faster. + + Co-Authored-By: Claude Opus 4.8 (1M context) + +commit 918ca8edddbf542d9034d8e58931edfea036ecca +Author: Anthony Green +Date: Sat Jun 20 12:34:46 2026 -0400 + + ci: actually zip the MSVC binaries before the release upload + + The MSVC 'Create binary distribution' step builds a *directory* + (libffi--x86-bit-msvc-binaries/), but 'Upload to GitHub Release' + pointed softprops/action-gh-release at a .zip file that was never created. With + no matching file the action just warns and succeeds, so on the v3.6.0 tag the + step reported success while attaching nothing. + + Add a pwsh Compress-Archive step to produce the .zip (contents at top level, like + the upload-artifact layout), and set fail_on_unmatched_files: true so a missing + file fails the step loudly instead of silently passing. + + Co-Authored-By: Claude Opus 4.8 (1M context) + commit f6303b8b0dd6353c6a5bb4de2e855a13b86f22cf Author: Anthony Green Date: Sat Jun 20 09:54:12 2026 -0400 diff --git a/deps/libffi/LICENSE b/deps/libffi/LICENSE index 12b4970e0ddcb4..90a8b230284e5c 100644 --- a/deps/libffi/LICENSE +++ b/deps/libffi/LICENSE @@ -1,4 +1,4 @@ -libffi - Copyright (c) 1996-2025 Anthony Green, Red Hat, Inc and others. +libffi - Copyright (c) 1996-2026 Anthony Green, Red Hat, Inc and others. See source files for details. Permission is hereby granted, free of charge, to any person obtaining diff --git a/deps/libffi/README.md b/deps/libffi/README.md index 7693f1daf6f850..19f78f632b0cd4 100644 --- a/deps/libffi/README.md +++ b/deps/libffi/README.md @@ -1,5 +1,5 @@ -libffi-3.6.0 was released on June 20, 2026. +libffi-3.7.1 was released on July 10, 2026. What is libffi? @@ -201,6 +201,30 @@ History See the git log for details at http://github.com/libffi/libffi. + 3.7.1 July-10-2026 + Fix aarch64 ffi_call memory corruption when passing many large + structs by value. + Fix i386 thiscall/fastcall closure stack cleanup for 64-bit + integer and struct arguments. + Fix aarch64 int128 argument split between x7 and the stack on + Darwin (#993). + Fix aarch64 clang-cl link failure for HFA helper functions (#996). + Build a generic ffi_call_plan fallback on Windows x86-64. + Add Windows ARM64 (MSVC) build and continuous integration support. + + 3.7.0 July-7-2026 + Add reusable call plans (ffi_call_plan_alloc/ffi_call_plan_invoke/ffi_call_plan_free). + Fix PA-RISC build broken by the conditional __int128 support added in 3.6.0. + Fix PA-RISC stack overflow passing many small structs by value. + Fix powerpc aix/darwin closure build errors (#987). + Include .note.GNU-stack on FreeBSD/x86 (#991). + Fix MSVC Win32 spin-lock atomics in bundled dlmalloc (#989). + Harden static trampoline and x86_64 closure internals. + Fix ffi_call clobbering the caller's argument pointer array + when passing large structs by value. + Allow a NULL rvalue in ffi_call to discard the return value. + Fix wasm widening of integral returns narrower than ffi_arg. + 3.6.0 Jun-20-2026 Add LoongArch32 support. Add RISC-V static trampoline support. diff --git a/deps/libffi/configure b/deps/libffi/configure index 15b227e6888f82..e050ddc8675da1 100755 --- a/deps/libffi/configure +++ b/deps/libffi/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.71 for libffi 3.6.0. +# Generated by GNU Autoconf 2.71 for libffi 3.7.1. # # Report bugs to . # @@ -621,8 +621,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='libffi' PACKAGE_TARNAME='libffi' -PACKAGE_VERSION='3.6.0' -PACKAGE_STRING='libffi 3.6.0' +PACKAGE_VERSION='3.7.1' +PACKAGE_STRING='libffi 3.7.1' PACKAGE_BUGREPORT='http://github.com/libffi/libffi/issues' PACKAGE_URL='' @@ -1417,7 +1417,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures libffi 3.6.0 to adapt to many kinds of systems. +\`configure' configures libffi 3.7.1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1489,7 +1489,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of libffi 3.6.0:";; + short | recursive ) echo "Configuration of libffi 3.7.1:";; esac cat <<\_ACEOF @@ -1628,7 +1628,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -libffi configure 3.6.0 +libffi configure 3.7.1 generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc. @@ -2259,7 +2259,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by libffi $as_me 3.6.0, which was +It was created by libffi $as_me 3.7.1, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw @@ -3233,8 +3233,11 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_headers="$ac_config_headers fficonfig.h" -FFI_VERSION_STRING="3.5.2" -FFI_VERSION_NUMBER=30502 +FFI_VERSION_STRING="3.7.1" +ffi_version_major=`echo "3.7.1" | cut -d. -f1` +ffi_version_minor=`echo "3.7.1" | cut -d. -f2 | sed 's/[^0-9].*//'` +ffi_version_micro=`echo "3.7.1" | cut -d. -f3 | sed 's/[^0-9].*//'` +FFI_VERSION_NUMBER=`expr ${ffi_version_major:-0} \* 10000 + ${ffi_version_minor:-0} \* 100 + ${ffi_version_micro:-0}` @@ -3983,7 +3986,7 @@ fi # Define the identity of the package. PACKAGE='libffi' - VERSION='3.6.0' + VERSION='3.7.1' printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h @@ -20658,7 +20661,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by libffi $as_me 3.6.0, which was +This file was extended by libffi $as_me 3.7.1, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -20726,7 +20729,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -libffi config.status 3.6.0 +libffi config.status 3.7.1 configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" diff --git a/deps/libffi/configure.ac b/deps/libffi/configure.ac index 2c8ba77970b3b0..3370acc3a39690 100644 --- a/deps/libffi/configure.ac +++ b/deps/libffi/configure.ac @@ -2,11 +2,17 @@ dnl Process this with autoconf to create configure AC_PREREQ([2.68]) -AC_INIT([libffi],[3.6.0],[http://github.com/libffi/libffi/issues]) +AC_INIT([libffi],[3.7.1],[http://github.com/libffi/libffi/issues]) AC_CONFIG_HEADERS([fficonfig.h]) -FFI_VERSION_STRING="3.5.2" -FFI_VERSION_NUMBER=30502 +dnl Derive the version macros from AC_INIT so they cannot drift when the +dnl release version is bumped. FFI_VERSION_NUMBER encodes X.Y.Z as +dnl X*10000 + Y*100 + Z, ignoring any non-numeric suffix (e.g. -rc0). +FFI_VERSION_STRING="AC_PACKAGE_VERSION" +ffi_version_major=`echo "AC_PACKAGE_VERSION" | cut -d. -f1` +ffi_version_minor=`echo "AC_PACKAGE_VERSION" | cut -d. -f2 | sed 's/[[^0-9]].*//'` +ffi_version_micro=`echo "AC_PACKAGE_VERSION" | cut -d. -f3 | sed 's/[[^0-9]].*//'` +FFI_VERSION_NUMBER=`expr ${ffi_version_major:-0} \* 10000 + ${ffi_version_minor:-0} \* 100 + ${ffi_version_micro:-0}` AC_SUBST(FFI_VERSION_STRING) AC_SUBST(FFI_VERSION_NUMBER) diff --git a/deps/libffi/doc/libffi.info b/deps/libffi/doc/libffi.info index b5b81d5bcf815f..b43246f8ed4e3a 100644 --- a/deps/libffi/doc/libffi.info +++ b/deps/libffi/doc/libffi.info @@ -3,7 +3,7 @@ This is libffi.info, produced by makeinfo version 7.1 from libffi.texi. This manual is for libffi, a portable foreign function interface library. - Copyright © 2008-2025 Anthony Green and Red Hat, Inc. + Copyright © 2008-2026 Anthony Green and Red Hat, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -38,7 +38,7 @@ libffi This manual is for libffi, a portable foreign function interface library. - Copyright © 2008-2025 Anthony Green and Red Hat, Inc. + Copyright © 2008-2026 Anthony Green and Red Hat, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -112,6 +112,7 @@ File: libffi.info, Node: Using libffi, Next: Memory Usage, Prev: Introduction * Simple Example:: A simple example. * Types:: libffi type descriptions. * Multiple ABIs:: Different passing styles on one platform. +* Reusable Call Plans:: Building a call plan once and reusing it. * The Closure API:: Writing a generic function. * Closure Example:: A closure example. * Thread Safety:: Thread safety. @@ -200,6 +201,11 @@ function: responsibility to ensure this. If CIF declares that the function returns ‘void’ (using ‘ffi_type_void’), then RVALUE is ignored. + RVALUE may also be ‘NULL’, in which case the call is performed and + the return value is discarded. This works for any return type, + including structures returned in memory; ‘libffi’ supplies internal + scratch space for the callee when needed. + In most situations, ‘libffi’ will handle promotion according to the ABI. However, for historical reasons, there is a special case with return values that must be handled by your code. In particular, @@ -213,7 +219,9 @@ function: AVALUES is a vector of ‘void *’ pointers that point to the memory locations holding the argument values for a call. If CIF declares that the function has no arguments (i.e., NARGS was 0), then - AVALUES is ignored. + AVALUES is ignored. ‘ffi_call’ does not modify the vector or the + argument values it points to, so both may be reused for subsequent + calls. Note that while the return value must be register-sized, arguments should exactly match their declared type. For example, if an @@ -739,7 +747,7 @@ compilers that support them: type descriptors in the previous example.  -File: libffi.info, Node: Multiple ABIs, Next: The Closure API, Prev: Types, Up: Using libffi +File: libffi.info, Node: Multiple ABIs, Next: Reusable Call Plans, Prev: Types, Up: Using libffi 2.4 Multiple ABIs ================= @@ -751,9 +759,46 @@ instance, the x86 platform has both ‘stdcall’ and ‘fastcall’ functions. necessarily platform-specific.  -File: libffi.info, Node: The Closure API, Next: Closure Example, Prev: Multiple ABIs, Up: Using libffi +File: libffi.info, Node: Reusable Call Plans, Next: The Closure API, Prev: Multiple ABIs, Up: Using libffi + +2.5 Reusable Call Plans +======================= + +When the same signature is called many times - as language bindings +typically do - you can build a “call plan” once and reuse it, so that +each call avoids the work ‘ffi_call’ would otherwise repeat on every +invocation. A plan is an opaque, caller-owned object built from a +prepared ‘ffi_cif’. + + -- Function: ffi_call_plan * ffi_call_plan_alloc (ffi_cif *CIF) + Builds and returns a reusable plan for the signature described by + CIF, which must already have been prepared with ‘ffi_prep_cif’. + The plan does not copy CIF; CIF must remain valid for as long as + the plan is used. + + Returns ‘NULL’ only when memory cannot be allocated. A signature + for which no accelerated path exists is still valid: the returned + plan simply falls back to ‘ffi_call’ when it is invoked. + + -- Function: void ffi_call_plan_invoke (ffi_call_plan *PLAN, void *FN, + void *RVALUE, void **AVALUES) + Calls FN using PLAN. The FN, RVALUE, and AVALUES arguments have + exactly the same meaning as for ‘ffi_call’ (*note The Basics::), + including the rule that integral return values narrower than a + register are widened to ‘ffi_arg’. + + A plan is immutable once built, so a single plan may be invoked + concurrently from multiple threads without additional locking. + + -- Function: void ffi_call_plan_free (ffi_call_plan *PLAN) + Releases a plan returned by ‘ffi_call_plan_alloc’. Passing ‘NULL’ + is harmless. The ‘ffi_cif’ the plan was built from is not + affected. + + +File: libffi.info, Node: The Closure API, Next: Closure Example, Prev: Reusable Call Plans, Up: Using libffi -2.5 The Closure API +2.6 The Closure API =================== ‘libffi’ also provides a way to write a generic function - a function @@ -857,7 +902,7 @@ executable addresses.  File: libffi.info, Node: Closure Example, Next: Thread Safety, Prev: The Closure API, Up: Using libffi -2.6 Closure Example +2.7 Closure Example =================== A trivial example that creates a new ‘puts’ by binding ‘fputs’ with @@ -916,7 +961,7 @@ A trivial example that creates a new ‘puts’ by binding ‘fputs’ with  File: libffi.info, Node: Thread Safety, Prev: Closure Example, Up: Using libffi -2.7 Thread Safety +2.8 Thread Safety ================= ‘libffi’ is not completely thread-safe. However, many parts are, and if @@ -1004,17 +1049,21 @@ Index * cif: The Basics. (line 14) * closure API: The Closure API. (line 13) * closures: The Closure API. (line 13) -* const char *: The Basics. (line 106) +* const char *: The Basics. (line 113) * FFI: Introduction. (line 31) * ffi_call: The Basics. (line 72) +* ffi_call_plan *: Reusable Call Plans. (line 12) +* ffi_call_plan_alloc: Reusable Call Plans. (line 12) +* ffi_call_plan_free: Reusable Call Plans. (line 32) +* ffi_call_plan_invoke: Reusable Call Plans. (line 22) * ffi_closure_alloc: The Closure API. (line 19) * ffi_closure_free: The Closure API. (line 26) * FFI_CLOSURES: The Closure API. (line 13) -* ffi_get_closure_size: The Basics. (line 118) -* ffi_get_default_abi: The Basics. (line 115) +* ffi_get_closure_size: The Basics. (line 125) +* ffi_get_default_abi: The Basics. (line 122) * ffi_get_struct_offsets: Size and Alignment. (line 39) -* ffi_get_version: The Basics. (line 106) -* ffi_get_version_number: The Basics. (line 110) +* ffi_get_version: The Basics. (line 113) +* ffi_get_version_number: The Basics. (line 117) * ffi_prep_cif: The Basics. (line 16) * ffi_prep_cif_var: The Basics. (line 39) * ffi_prep_closure_loc: The Closure API. (line 41) @@ -1051,12 +1100,14 @@ Index * ffi_type_ushort: Primitive Types. (line 53) * ffi_type_void: Primitive Types. (line 10) * Foreign Function Interface: Introduction. (line 31) -* size_t: The Basics. (line 118) -* unsigned int: The Basics. (line 115) -* unsigned long: The Basics. (line 110) +* size_t: The Basics. (line 125) +* unsigned int: The Basics. (line 122) +* unsigned long: The Basics. (line 117) * void: The Basics. (line 72) -* void <1>: The Closure API. (line 19) -* void <2>: The Closure API. (line 26) +* void <1>: Reusable Call Plans. (line 22) +* void <2>: Reusable Call Plans. (line 32) +* void <3>: The Closure API. (line 19) +* void <4>: The Closure API. (line 26)  @@ -1064,23 +1115,24 @@ Tag Table: Node: Top1387 Node: Introduction2909 Node: Using libffi4569 -Node: The Basics5098 -Node: Simple Example10868 -Node: Types11925 -Node: Primitive Types12436 -Node: Structures14753 -Node: Size and Alignment15864 -Node: Arrays Unions Enums18135 -Node: Type Example21112 -Node: Complex22418 -Node: Complex Type Example23932 -Node: Multiple ABIs26984 -Node: The Closure API27367 -Node: Closure Example31703 -Node: Thread Safety33347 -Node: Memory Usage34180 -Node: Missing Features35455 -Node: Index35832 +Node: The Basics5172 +Node: Simple Example11346 +Node: Types12403 +Node: Primitive Types12914 +Node: Structures15231 +Node: Size and Alignment16342 +Node: Arrays Unions Enums18613 +Node: Type Example21590 +Node: Complex22896 +Node: Complex Type Example24410 +Node: Multiple ABIs27462 +Node: Reusable Call Plans27849 +Node: The Closure API29566 +Node: Closure Example33908 +Node: Thread Safety35552 +Node: Memory Usage36385 +Node: Missing Features37660 +Node: Index38037  End Tag Table diff --git a/deps/libffi/doc/libffi.pdf b/deps/libffi/doc/libffi.pdf index 3aa5406e61e086..250d34faf71159 100644 Binary files a/deps/libffi/doc/libffi.pdf and b/deps/libffi/doc/libffi.pdf differ diff --git a/deps/libffi/doc/libffi.texi b/deps/libffi/doc/libffi.texi index 73b128f863e7be..251214f890d3f5 100644 --- a/deps/libffi/doc/libffi.texi +++ b/deps/libffi/doc/libffi.texi @@ -18,7 +18,7 @@ This manual is for libffi, a portable foreign function interface library. -Copyright @copyright{} 2008--2025 Anthony Green and Red Hat, Inc. +Copyright @copyright{} 2008--2026 Anthony Green and Red Hat, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -118,6 +118,7 @@ values passed between the two languages. * Simple Example:: A simple example. * Types:: libffi type descriptions. * Multiple ABIs:: Different passing styles on one platform. +* Reusable Call Plans:: Building a call plan once and reusing it. * The Closure API:: Writing a generic function. * Closure Example:: A closure example. * Thread Safety:: Thread safety. @@ -213,6 +214,11 @@ to ensure this. If @var{cif} declares that the function returns @code{void} (using @code{ffi_type_void}), then @var{rvalue} is ignored. +@var{rvalue} may also be @code{NULL}, in which case the call is +performed and the return value is discarded. This works for any +return type, including structures returned in memory; @code{libffi} +supplies internal scratch space for the callee when needed. + In most situations, @code{libffi} will handle promotion according to the ABI. However, for historical reasons, there is a special case with return values that must be handled by your code. In particular, @@ -226,7 +232,9 @@ full @code{ffi_arg} into the return value. @var{avalues} is a vector of @code{void *} pointers that point to the memory locations holding the argument values for a call. If @var{cif} declares that the function has no arguments (i.e., @var{nargs} was 0), -then @var{avalues} is ignored. +then @var{avalues} is ignored. @code{ffi_call} does not modify the +vector or the argument values it points to, so both may be reused for +subsequent calls. Note that while the return value must be register-sized, arguments should exactly match their declared type. For example, if an argument @@ -806,6 +814,45 @@ necessarily platform-specific. @c FIXME: document the platforms +@node Reusable Call Plans +@section Reusable Call Plans + +When the same signature is called many times -- as language bindings +typically do -- you can build a @dfn{call plan} once and reuse it, so +that each call avoids the work @code{ffi_call} would otherwise repeat +on every invocation. A plan is an opaque, caller-owned object built +from a prepared @code{ffi_cif}. + +@findex ffi_call_plan_alloc +@defun {ffi_call_plan *} ffi_call_plan_alloc (ffi_cif *@var{cif}) +Builds and returns a reusable plan for the signature described by +@var{cif}, which must already have been prepared with +@code{ffi_prep_cif}. The plan does not copy @var{cif}; @var{cif} must +remain valid for as long as the plan is used. + +Returns @code{NULL} only when memory cannot be allocated. A signature +for which no accelerated path exists is still valid: the returned plan +simply falls back to @code{ffi_call} when it is invoked. +@end defun + +@findex ffi_call_plan_invoke +@defun void ffi_call_plan_invoke (ffi_call_plan *@var{plan}, void *@var{fn}, void *@var{rvalue}, void **@var{avalues}) +Calls @var{fn} using @var{plan}. The @var{fn}, @var{rvalue}, and +@var{avalues} arguments have exactly the same meaning as for +@code{ffi_call} (@pxref{The Basics}), including the rule that integral +return values narrower than a register are widened to @code{ffi_arg}. + +A plan is immutable once built, so a single plan may be invoked +concurrently from multiple threads without additional locking. +@end defun + +@findex ffi_call_plan_free +@defun void ffi_call_plan_free (ffi_call_plan *@var{plan}) +Releases a plan returned by @code{ffi_call_plan_alloc}. Passing +@code{NULL} is harmless. The @code{ffi_cif} the plan was built from is +not affected. +@end defun + @node The Closure API @section The Closure API diff --git a/deps/libffi/doc/stamp-vti b/deps/libffi/doc/stamp-vti index 6658c1da7e7825..e755454e9c8215 100644 --- a/deps/libffi/doc/stamp-vti +++ b/deps/libffi/doc/stamp-vti @@ -1,4 +1,4 @@ -@set UPDATED 20 June 2026 -@set UPDATED-MONTH June 2026 -@set EDITION 3.6.0 -@set VERSION 3.6.0 +@set UPDATED 10 July 2026 +@set UPDATED-MONTH July 2026 +@set EDITION 3.7.1 +@set VERSION 3.7.1 diff --git a/deps/libffi/doc/version.texi b/deps/libffi/doc/version.texi index 6658c1da7e7825..e755454e9c8215 100644 --- a/deps/libffi/doc/version.texi +++ b/deps/libffi/doc/version.texi @@ -1,4 +1,4 @@ -@set UPDATED 20 June 2026 -@set UPDATED-MONTH June 2026 -@set EDITION 3.6.0 -@set VERSION 3.6.0 +@set UPDATED 10 July 2026 +@set UPDATED-MONTH July 2026 +@set EDITION 3.7.1 +@set VERSION 3.7.1 diff --git a/deps/libffi/generate-headers.py b/deps/libffi/generate-headers.py index 312b0099ef2f0c..e2d2942deffb84 100644 --- a/deps/libffi/generate-headers.py +++ b/deps/libffi/generate-headers.py @@ -7,8 +7,8 @@ from pathlib import Path -LIBFFI_VERSION = '3.6.0' -LIBFFI_VERSION_NUMBER = '30600' +LIBFFI_VERSION = '3.7.1' +LIBFFI_VERSION_NUMBER = '30701' def normalize_arch(target_arch): aliases = { diff --git a/deps/libffi/include/ffi.h.in b/deps/libffi/include/ffi.h.in index 4ac9c2598c1517..35f09cf43315ca 100644 --- a/deps/libffi/include/ffi.h.in +++ b/deps/libffi/include/ffi.h.in @@ -1,6 +1,6 @@ /* -----------------------------------------------------------------*-C-*- libffi @VERSION@ - - Copyright (c) 2011, 2014, 2019, 2021, 2022, 2024, 2025 Anthony Green + - Copyright (c) 2011, 2014, 2019, 2021, 2022, 2024, 2025, 2026 Anthony Green - Copyright (c) 1996-2003, 2007, 2008 Red Hat, Inc. Permission is hereby granted, free of charge, to any person @@ -524,6 +524,32 @@ void ffi_call(ffi_cif *cif, void *rvalue, void **avalue); +/* Reusable call plans. + + A plan captures a signature's argument placement once so that repeated + calls skip the per-call work that ffi_call would otherwise redo. Build one + with ffi_call_plan_alloc, invoke it as many times as you like, and release + it with ffi_call_plan_free. + + The plan is opaque and caller-owned. The cif must outlive the plan. + ffi_call_plan_alloc returns NULL only on allocation failure; a signature + with no fast path is still valid and ffi_call_plan_invoke falls back to + ffi_call for it. A plan is immutable once built, so it may be shared and + invoked concurrently from multiple threads. */ +typedef struct ffi_call_plan ffi_call_plan; + +FFI_API +ffi_call_plan *ffi_call_plan_alloc (ffi_cif *cif); + +FFI_API +void ffi_call_plan_invoke (ffi_call_plan *plan, + void (*fn)(void), + void *rvalue, + void **avalue); + +FFI_API +void ffi_call_plan_free (ffi_call_plan *plan); + FFI_API ffi_status ffi_get_struct_offsets (ffi_abi abi, ffi_type *struct_type, size_t *offsets); diff --git a/deps/libffi/libffi.gyp b/deps/libffi/libffi.gyp index 2d43dcdb733c97..f3122a4e2a0041 100644 --- a/deps/libffi/libffi.gyp +++ b/deps/libffi/libffi.gyp @@ -204,13 +204,6 @@ }, ], 'conditions': [ - ['OS == "win" and clang == 1', { - 'msvs_settings': { - 'VCCLCompilerTool': { - 'AdditionalOptions': ['-Wno-incompatible-pointer-types'], - }, - }, - }], ['OS == "win" and (target_arch == "x64" or target_arch == "x86_64")', { 'actions': [ { diff --git a/deps/libffi/libffi.map.in b/deps/libffi/libffi.map.in index 838acfea503602..f4e366eb60bceb 100644 --- a/deps/libffi/libffi.map.in +++ b/deps/libffi/libffi.map.in @@ -58,6 +58,17 @@ LIBFFI_BASE_8.1 { ffi_get_closure_size; } LIBFFI_BASE_8.0; +/* ---------------------------------------------------------------------- + Reusable call plans (ffi_call_plan_*). Additive: new symbols only, the + existing struct layout and entry points are untouched. + -------------------------------------------------------------------- */ +LIBFFI_CALL_PLAN_8.4 { + global: + ffi_call_plan_alloc; + ffi_call_plan_invoke; + ffi_call_plan_free; +} LIBFFI_BASE_8.1; + #ifdef FFI_TARGET_HAS_COMPLEX_TYPE LIBFFI_COMPLEX_8.0 { global: diff --git a/deps/libffi/libtool-version b/deps/libffi/libtool-version index 656cc6056f7401..c5545eab8bf6c2 100644 --- a/deps/libffi/libtool-version +++ b/deps/libffi/libtool-version @@ -26,4 +26,4 @@ # release, then set age to 0. # # CURRENT:REVISION:AGE -11:1:3 +12:1:4 diff --git a/deps/libffi/msvcc.sh b/deps/libffi/msvcc.sh index 301e2fbd6840fe..a69f57fd98882e 100755 --- a/deps/libffi/msvcc.sh +++ b/deps/libffi/msvcc.sh @@ -94,6 +94,13 @@ do cl="clang-cl" shift 1 ;; + -E) + # DejaGnu-style preprocessing (-E -o ): handled specially + # below, since cl writes preprocessed output to stdout and prints + # the source file name to stderr. + preprocess="true" + shift 1 + ;; -O0) args="$args -Od" shift 1 @@ -236,19 +243,25 @@ do shift 1 ;; -S) - args="$args -FAs" + # Compile to assembly without linking, like gcc -S: emit the + # listing and stop after the compile step. + args="$args -c -FAs" + compile_to_asm="true" shift 1 ;; -o) outdir="$(dirname $2)" base="$(basename $2|sed 's/\.[^.]*//g')" - if [ -n "$single" ]; then + ppout="$2" + if [ -n "$compile_to_asm" ]; then + output="-Fo$outdir/$base.obj -Fa$2" + elif [ -n "$single" ]; then output="-Fo$2" else output="-Fe$2" fi armasm_output="-o $2" - if [ -n "$assembly" ]; then + if [ -n "$assembly" ] || [ -n "$compile_to_asm" ]; then args="$args $output" else args="$args $output -Fd$outdir/$base -Fp$outdir/$base -Fa$outdir/$base" @@ -294,7 +307,19 @@ if [ -n "$debug_crt" ]; then md="${md}d" fi -if [ -n "$assembly" ]; then +if [ -n "$preprocess" ]; then + # Write the preprocessed output to the -o target (or stdout), and + # swallow stderr: cl prints the source file name there, which the + # testsuite's feature probes would mistake for a diagnostic. + args="$(echo $args | sed 's%[-/]F[edpoa][^ ]*%%g')" + + if test -n "$verbose"; then + echo "$cl -EP $args > ${ppout:-/dev/stdout}" + fi + + eval "\"$cl\" -EP $args" > "${ppout:-/dev/stdout}" 2>/dev/null + result=$? +elif [ -n "$assembly" ]; then if [ -z "$outdir" ]; then outdir="." fi diff --git a/deps/libffi/src/aarch64/ffi.c b/deps/libffi/src/aarch64/ffi.c index 715e478c0edd14..2e6a2ad2624c07 100644 --- a/deps/libffi/src/aarch64/ffi.c +++ b/deps/libffi/src/aarch64/ffi.c @@ -378,13 +378,13 @@ extend_integer_type (void *source, int type) } } -#if defined(_MSC_VER) +#if defined(_MSC_VER) && !defined(__clang__) void extend_hfa_type (void *dest, void *src, int h); #else static void extend_hfa_type (void *dest, void *src, int h) { - ssize_t f = h - AARCH64_RET_S4; + ptrdiff_t f = h - AARCH64_RET_S4; void *x0; #define BTI_J "hint #36" @@ -449,7 +449,7 @@ extend_hfa_type (void *dest, void *src, int h) } #endif -#if defined(_MSC_VER) +#if defined(_MSC_VER) && !defined(__clang__) void* compress_hfa_type (void *dest, void *src, int h); #else static void * @@ -550,7 +550,9 @@ allocate_int128_to_reg_or_stack (struct call_context *context, ngrn += ngrn & 1; #endif - if (ngrn < N_X_ARG_REG) + /* The value must fit entirely in registers, i.e. the low half may + not be allocated to x7 with the high half spilled to the stack. */ + if (ngrn + 2 <= N_X_ARG_REG) { ret = &context->x[ngrn]; ngrn += 2; @@ -642,6 +644,23 @@ ffi_prep_cif_machdep (ffi_cif *cif) break; } + /* Composites larger than 16 bytes (that are not HFAs) are passed by + invisible reference: ffi_call copies the payload into the argument + slab (next_struct_area, growing down) and, once the X registers are + exhausted, also spills the by-ref pointer into the same slab (the + NSAA, growing up). The generic prep_cif accounting in cif->bytes + only charges the payload copy, not that 8-byte pointer slot, so the + two regions can collide and a later struct copy can overwrite an + already-spilled pointer with the copied payload bytes. Reserve an + extra 8 bytes per such argument so the slab is always large enough + for both. */ + for (i = 0, n = cif->nargs; i < n; i++) + { + ffi_type *ty = cif->arg_types[i]; + if (ty->size > 16 && !is_vfp_type (ty)) + bytes += 8; + } + /* Round the stack up to a multiple of the stack alignment requirement. */ cif->bytes = (unsigned) FFI_ALIGN(bytes, 16); cif->flags = flags; diff --git a/deps/libffi/src/alpha/ffi.c b/deps/libffi/src/alpha/ffi.c index cb683ec14317f8..5830ee3f2109e4 100644 --- a/deps/libffi/src/alpha/ffi.c +++ b/deps/libffi/src/alpha/ffi.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - ffi.c - Copyright (c) 2012 Anthony Green + ffi.c - Copyright (c) 2012, 2026 Anthony Green Copyright (c) 1998, 2001, 2007, 2008 Red Hat, Inc. Alpha Foreign Function Interface diff --git a/deps/libffi/src/alpha/ffitarget.h b/deps/libffi/src/alpha/ffitarget.h index f80f13e85fe67e..da63dc08813a26 100644 --- a/deps/libffi/src/alpha/ffitarget.h +++ b/deps/libffi/src/alpha/ffitarget.h @@ -1,5 +1,5 @@ /* -----------------------------------------------------------------*-C-*- - ffitarget.h - Copyright (c) 2012 Anthony Green + ffitarget.h - Copyright (c) 2012, 2026 Anthony Green Copyright (c) 1996-2003 Red Hat, Inc. Target configuration macros for Alpha. diff --git a/deps/libffi/src/closures.c b/deps/libffi/src/closures.c index 124e1f386a7138..c1fbcf87ce29b6 100644 --- a/deps/libffi/src/closures.c +++ b/deps/libffi/src/closures.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - closures.c - Copyright (c) 2019, 2022 Anthony Green + closures.c - Copyright (c) 2019, 2022, 2026 Anthony Green Copyright (c) 2007, 2009, 2010 Red Hat, Inc. Copyright (C) 2007, 2009, 2010 Free Software Foundation, Inc Copyright (c) 2011 Plausible Labs Cooperative, Inc. diff --git a/deps/libffi/src/dlmalloc.c b/deps/libffi/src/dlmalloc.c index 5b400d65de3db4..93e81967039359 100644 --- a/deps/libffi/src/dlmalloc.c +++ b/deps/libffi/src/dlmalloc.c @@ -1916,8 +1916,8 @@ static FORCEINLINE void x86_clear_lock(int* sl) { #define CLEAR_LOCK(sl) x86_clear_lock(sl) #else /* Win32 MSC */ -#define CAS_LOCK(sl) interlockedexchange(sl, (LONG)1) -#define CLEAR_LOCK(sl) interlockedexchange (sl, (LONG)0) +#define CAS_LOCK(sl) interlockedexchange((LONG volatile *)(sl), (LONG)1) +#define CLEAR_LOCK(sl) interlockedexchange ((LONG volatile *)(sl), (LONG)0) #endif /* ... gcc spins locks ... */ diff --git a/deps/libffi/src/frv/ffi.c b/deps/libffi/src/frv/ffi.c index 99dd41d32d4c8f..4eb8e1ddfb87c8 100644 --- a/deps/libffi/src/frv/ffi.c +++ b/deps/libffi/src/frv/ffi.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - ffi.c - Copyright (C) 2004 Anthony Green + ffi.c - Copyright (C) 2004, 2026 Anthony Green Copyright (C) 2007 Free Software Foundation, Inc. Copyright (C) 2008 Red Hat, Inc. diff --git a/deps/libffi/src/ia64/ffi.c b/deps/libffi/src/ia64/ffi.c index c00ef08543289a..00e1ae15707c5f 100644 --- a/deps/libffi/src/ia64/ffi.c +++ b/deps/libffi/src/ia64/ffi.c @@ -1,7 +1,7 @@ /* ----------------------------------------------------------------------- ffi.c - Copyright (c) 1998, 2007, 2008, 2012 Red Hat, Inc. Copyright (c) 2000 Hewlett Packard Company - Copyright (c) 2011 Anthony Green + Copyright (c) 2011, 2026 Anthony Green IA64 Foreign Function Interface diff --git a/deps/libffi/src/m32r/ffi.c b/deps/libffi/src/m32r/ffi.c index 6fab50b115ed24..21d499abe00302 100644 --- a/deps/libffi/src/m32r/ffi.c +++ b/deps/libffi/src/m32r/ffi.c @@ -1,7 +1,7 @@ /* ----------------------------------------------------------------------- ffi.c - Copyright (c) 2004 Renesas Technology Copyright (c) 2008 Red Hat, Inc. - Copyright (c) 2022 Anthony Green + Copyright (c) 2022, 2026 Anthony Green M32R Foreign Function Interface @@ -180,10 +180,10 @@ void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) { extended_cif ecif; ffi_type **arg_types = cif->arg_types; + void **avalue_copy = NULL; int i, nargs = cif->nargs; ecif.cif = cif; - ecif.avalue = avalue; /* If the return value is a struct and we don't have a return value address then we need to make one. */ @@ -196,7 +196,8 @@ void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) ecif.rvalue = rvalue; /* If we have any large structure arguments, make a copy so we are passing - by value. */ + by value. The pointer array is cloned first: the caller owns avalue[] + and may reuse it for another call, so it must not be modified. */ for (i = 0; i < nargs; i++) { ffi_type *at = arg_types[i]; @@ -204,10 +205,17 @@ void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) if (at->type == FFI_TYPE_STRUCT && size > 4) { char *argcopy = alloca (size); + if (avalue_copy == NULL) + { + avalue_copy = alloca (nargs * sizeof (void *)); + memcpy (avalue_copy, avalue, nargs * sizeof (void *)); + avalue = avalue_copy; + } memcpy (argcopy, avalue[i], size); avalue[i] = argcopy; } } + ecif.avalue = avalue; switch (cif->abi) { diff --git a/deps/libffi/src/moxie/ffi.c b/deps/libffi/src/moxie/ffi.c index ace7b23f855763..582c06f5dcebab 100644 --- a/deps/libffi/src/moxie/ffi.c +++ b/deps/libffi/src/moxie/ffi.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - ffi.c - Copyright (C) 2012, 2013, 2018, 2021, 2022 Anthony Green + ffi.c - Copyright (C) 2012, 2013, 2018, 2021, 2022, 2026 Anthony Green Moxie Foreign Function Interface @@ -129,10 +129,10 @@ void ffi_call(ffi_cif *cif, { extended_cif ecif; ffi_type **arg_types = cif->arg_types; + void **avalue_copy = NULL; int i, nargs = cif->nargs; ecif.cif = cif; - ecif.avalue = avalue; /* If the return value is a struct and we don't have a return */ /* value address then we need to make one */ @@ -146,7 +146,8 @@ void ffi_call(ffi_cif *cif, ecif.rvalue = rvalue; /* If we have any large structure arguments, make a copy so we are passing - by value. */ + by value. The pointer array is cloned first: the caller owns avalue[] + and may reuse it for another call, so it must not be modified. */ for (i = 0; i < nargs; i++) { ffi_type *at = arg_types[i]; @@ -154,10 +155,17 @@ void ffi_call(ffi_cif *cif, if (at->type == FFI_TYPE_STRUCT) /* && size > 4) All struct args?? */ { char *argcopy = alloca (size); + if (avalue_copy == NULL) + { + avalue_copy = alloca (nargs * sizeof (void *)); + memcpy (avalue_copy, avalue, nargs * sizeof (void *)); + avalue = avalue_copy; + } memcpy (argcopy, avalue[i], size); avalue[i] = argcopy; } } + ecif.avalue = avalue; switch (cif->abi) { diff --git a/deps/libffi/src/or1k/ffi.c b/deps/libffi/src/or1k/ffi.c index 0393ea4af42af6..9303880a5e82b8 100644 --- a/deps/libffi/src/or1k/ffi.c +++ b/deps/libffi/src/or1k/ffi.c @@ -121,6 +121,7 @@ void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) int i; int size; ffi_type **arg; + void **avalue_copy = NULL; /* Calculate size to allocate on stack */ @@ -135,13 +136,21 @@ void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) size += 8; /* If we have any large structure arguments, make a copy so we are passing - by value. */ + by value. The pointer array is cloned first: the caller owns + avalue[] and may reuse it for another call, so it must not be + modified. */ { ffi_type *at = cif->arg_types[i]; int size = at->size; if (at->type == FFI_TYPE_STRUCT) /* && size > 4) All struct args? */ { char *argcopy = alloca (size); + if (avalue_copy == NULL) + { + avalue_copy = alloca (cif->nargs * sizeof (void *)); + memcpy (avalue_copy, avalue, cif->nargs * sizeof (void *)); + avalue = avalue_copy; + } memcpy (argcopy, avalue[i], size); avalue[i] = argcopy; } diff --git a/deps/libffi/src/pa/ffi.c b/deps/libffi/src/pa/ffi.c index 5460ca2fdbe147..e5de1dc72eb38e 100644 --- a/deps/libffi/src/pa/ffi.c +++ b/deps/libffi/src/pa/ffi.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - ffi.c - (c) 2011 Anthony Green + ffi.c - (c) 2011, 2026 Anthony Green (c) 2008 Red Hat, Inc. (c) 2006 Free Software Foundation, Inc. (c) 2003-2004 Randolph Chung @@ -280,9 +280,24 @@ static void ffi_size_stack_pa32(ffi_cif *cif) #ifdef PA_HPUX case FFI_TYPE_LONGDOUBLE: + z += 1; /* passed by pointer, like a large struct */ + break; #endif + case FFI_TYPE_STRUCT: - z += 1; /* pass by ptr, callee will copy */ + /* This must mirror the slot accounting in ffi_prep_args_pa32: + structs of 1-4 bytes occupy one slot, structs of 5-8 bytes are + passed inline in two even-aligned slots (exactly like a 64-bit + value), and larger structs are passed by pointer in one slot. + z stays offset from the marshaller's slot by FIRST_ARG_SLOT (odd), + so (z & 1) tracks the same alignment the marshaller applies. */ + { + size_t len = (*ptr)->size; + if (len <= 4 || len > 8) + z += 1; + else + z += 2 + (z & 1); + } break; default: /* <= 32-bit values */ @@ -363,12 +378,13 @@ void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) extended_cif ecif; size_t i, nargs = cif->nargs; ffi_type **arg_types = cif->arg_types; + void **avalue_copy = NULL; ecif.cif = cif; - ecif.avalue = avalue; /* If we have any large structure arguments, make a copy so we are passing - by value. */ + by value. The pointer array is cloned first: the caller owns avalue[] + and may reuse it for another call, so it must not be modified. */ for (i = 0; i < nargs; i++) { ffi_type *at = arg_types[i]; @@ -376,10 +392,17 @@ void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) if (at->type == FFI_TYPE_STRUCT && size > 8) { char *argcopy = alloca (size); + if (avalue_copy == NULL) + { + avalue_copy = alloca (nargs * sizeof (void *)); + memcpy (avalue_copy, avalue, nargs * sizeof (void *)); + avalue = avalue_copy; + } memcpy (argcopy, avalue[i], size); avalue[i] = argcopy; } } + ecif.avalue = avalue; /* If the return value is a struct and we don't have a return value address then we need to make one. */ diff --git a/deps/libffi/src/pa/ffitarget.h b/deps/libffi/src/pa/ffitarget.h index dae854a695bb30..f6f09975cfac36 100644 --- a/deps/libffi/src/pa/ffitarget.h +++ b/deps/libffi/src/pa/ffitarget.h @@ -1,5 +1,5 @@ /* -----------------------------------------------------------------*-C-*- - ffitarget.h - Copyright (c) 2012 Anthony Green + ffitarget.h - Copyright (c) 2012, 2026 Anthony Green Copyright (c) 1996-2003 Red Hat, Inc. Target configuration macros for hppa. @@ -82,11 +82,19 @@ typedef enum ffi_abi { #define FFI_TYPE_SMALL_STRUCT7 -7 #define FFI_TYPE_SMALL_STRUCT8 -8 -/* linux.S and hpux32.S expect FFI_TYPE_COMPLEX is the last generic type. */ -#define FFI_PA_TYPE_LAST FFI_TYPE_COMPLEX - -/* If new generic types are added, the jump tables in linux.S and hpux32.S - likely need updating. */ +/* The return-value jump tables in linux.S and hpux32.S are indexed by + cif->flags, which ffi_prep_cif_machdep derives from the return type. Any + return type it does not handle explicitly -- including FFI_TYPE_COMPLEX and + the 128-bit integer types FFI_TYPE_UINT128/FFI_TYPE_SINT128 -- falls through + to the default case and is mapped to FFI_TYPE_INT, so cif->flags never + exceeds FFI_TYPE_COMPLEX and the existing tables remain sufficient. Bump + FFI_PA_TYPE_LAST to the current FFI_TYPE_LAST once you have confirmed any + newly added generic type is likewise handled (or the tables extended). */ +#define FFI_PA_TYPE_LAST FFI_TYPE_SINT128 + +/* Tripwire: when a new generic type is added FFI_TYPE_LAST changes and this + fires, forcing a review of ffi_prep_cif_machdep and the linux.S / hpux32.S + jump tables before FFI_PA_TYPE_LAST above is bumped. */ #if FFI_TYPE_LAST != FFI_PA_TYPE_LAST # error "You likely have broken jump tables" #endif diff --git a/deps/libffi/src/powerpc/ffi.c b/deps/libffi/src/powerpc/ffi.c index 1afa582ebead82..69725242344c32 100644 --- a/deps/libffi/src/powerpc/ffi.c +++ b/deps/libffi/src/powerpc/ffi.c @@ -1,6 +1,6 @@ /* ----------------------------------------------------------------------- ffi.c - Copyright (C) 2013 IBM - Copyright (C) 2011 Anthony Green + Copyright (C) 2011, 2026 Anthony Green Copyright (C) 2011 Kyle Moffett Copyright (C) 2008 Red Hat, Inc Copyright (C) 2007, 2008 Free Software Foundation, Inc diff --git a/deps/libffi/src/powerpc/ffi_darwin.c b/deps/libffi/src/powerpc/ffi_darwin.c index d62737f47150c2..01e2a43701d7a1 100644 --- a/deps/libffi/src/powerpc/ffi_darwin.c +++ b/deps/libffi/src/powerpc/ffi_darwin.c @@ -4,6 +4,7 @@ Copyright (C) 1998 Geoffrey Keating Copyright (C) 2001 John Hornkvist Copyright (C) 2002, 2006, 2007, 2009, 2010 Free Software Foundation, Inc. + Copyright (C) 2026 Anthony Green FFI support for Darwin and AIX. diff --git a/deps/libffi/src/powerpc/ffi_linux64.c b/deps/libffi/src/powerpc/ffi_linux64.c index 4a4fe86beff1f2..b1f1468ed5f856 100644 --- a/deps/libffi/src/powerpc/ffi_linux64.c +++ b/deps/libffi/src/powerpc/ffi_linux64.c @@ -1,6 +1,6 @@ /* ----------------------------------------------------------------------- ffi_linux64.c - Copyright (C) 2013 IBM - Copyright (C) 2011 Anthony Green + Copyright (C) 2011, 2026 Anthony Green Copyright (C) 2011 Kyle Moffett Copyright (C) 2008 Red Hat, Inc Copyright (C) 2007, 2008 Free Software Foundation, Inc diff --git a/deps/libffi/src/powerpc/ffi_powerpc.h b/deps/libffi/src/powerpc/ffi_powerpc.h index c366315baefefe..bad33efe3b8cc1 100644 --- a/deps/libffi/src/powerpc/ffi_powerpc.h +++ b/deps/libffi/src/powerpc/ffi_powerpc.h @@ -1,6 +1,6 @@ /* ----------------------------------------------------------------------- ffi_powerpc.h - Copyright (C) 2013 IBM - Copyright (C) 2011 Anthony Green + Copyright (C) 2011, 2026 Anthony Green Copyright (C) 2011 Kyle Moffett Copyright (C) 2008 Red Hat, Inc Copyright (C) 2007, 2008 Free Software Foundation, Inc diff --git a/deps/libffi/src/powerpc/ffi_sysv.c b/deps/libffi/src/powerpc/ffi_sysv.c index 975c58c2c0e049..a33039e3dbdb1a 100644 --- a/deps/libffi/src/powerpc/ffi_sysv.c +++ b/deps/libffi/src/powerpc/ffi_sysv.c @@ -1,6 +1,6 @@ /* ----------------------------------------------------------------------- ffi_sysv.c - Copyright (C) 2013 IBM - Copyright (C) 2011 Anthony Green + Copyright (C) 2011, 2026 Anthony Green Copyright (C) 2011 Kyle Moffett Copyright (C) 2008 Red Hat, Inc Copyright (C) 2007, 2008 Free Software Foundation, Inc diff --git a/deps/libffi/src/powerpc/ffitarget.h b/deps/libffi/src/powerpc/ffitarget.h index cf67a9bba6ee33..7ffe36b682e4a7 100644 --- a/deps/libffi/src/powerpc/ffitarget.h +++ b/deps/libffi/src/powerpc/ffitarget.h @@ -1,5 +1,5 @@ /* -----------------------------------------------------------------*-C-*- - ffitarget.h - Copyright (c) 2012 Anthony Green + ffitarget.h - Copyright (c) 2012, 2026 Anthony Green Copyright (C) 2007, 2008, 2010 Free Software Foundation, Inc Copyright (c) 1996-2003 Red Hat, Inc. diff --git a/deps/libffi/src/prep_cif.c b/deps/libffi/src/prep_cif.c index 47c9259470c5ee..1836270d1a9cb7 100644 --- a/deps/libffi/src/prep_cif.c +++ b/deps/libffi/src/prep_cif.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - prep_cif.c - Copyright (c) 2011, 2012, 2021, 2025 Anthony Green + prep_cif.c - Copyright (c) 2011, 2012, 2021, 2025, 2026 Anthony Green Copyright (c) 1996, 1998, 2007 Red Hat, Inc. Copyright (c) 2022 Oracle and/or its affiliates. @@ -278,3 +278,42 @@ ffi_get_struct_offsets (ffi_abi abi, ffi_type *struct_type, size_t *offsets) return initialize_aggregate(struct_type, offsets); } + +/* Generic ffi_call_plan: a portable fallback compiled on every target that does + not provide its own accelerated implementation. The x86-64 SysV backend + (ffi64.c) defines these with a fast path under __x86_64__ && !__ILP32__, but + that file is not built for Windows x86-64 (X86_WIN64), which uses ffiw64.c + instead -- and clang-cl and MSYS/mingw both define __x86_64__ there. So + exclude the fallback only when ffi64.c actually provides it; everywhere else + this plan just records the cif and invoke calls ffi_call, so the API is + always present and links on all targets. The cif must outlive the plan. */ +#if !(defined(__x86_64__) && !defined(__ILP32__) && !defined(X86_WIN64)) + +struct ffi_call_plan +{ + ffi_cif *cif; +}; + +ffi_call_plan * +ffi_call_plan_alloc (ffi_cif *cif) +{ + ffi_call_plan *plan = malloc (sizeof (struct ffi_call_plan)); + if (plan != NULL) + plan->cif = cif; + return plan; +} + +void +ffi_call_plan_invoke (ffi_call_plan *plan, void (*fn) (void), + void *rvalue, void **avalue) +{ + ffi_call (plan->cif, fn, rvalue, avalue); +} + +void +ffi_call_plan_free (ffi_call_plan *plan) +{ + free (plan); +} + +#endif /* generic ffi_call_plan fallback */ diff --git a/deps/libffi/src/s390/ffitarget.h b/deps/libffi/src/s390/ffitarget.h index 6a9c3234289cb5..4fc8ef02cfe83b 100644 --- a/deps/libffi/src/s390/ffitarget.h +++ b/deps/libffi/src/s390/ffitarget.h @@ -1,5 +1,5 @@ /* -----------------------------------------------------------------*-C-*- - ffitarget.h - Copyright (c) 2012 Anthony Green + ffitarget.h - Copyright (c) 2012, 2026 Anthony Green Copyright (c) 1996-2003 Red Hat, Inc. Target configuration macros for S390. diff --git a/deps/libffi/src/sh64/ffi.c b/deps/libffi/src/sh64/ffi.c index 4545b0fa72141b..2df6e23a3fda29 100644 --- a/deps/libffi/src/sh64/ffi.c +++ b/deps/libffi/src/sh64/ffi.c @@ -1,6 +1,6 @@ /* ----------------------------------------------------------------------- ffi.c - Copyright (c) 2003, 2004, 2006, 2007, 2012 Kaz Kojima - Copyright (c) 2008 Anthony Green + Copyright (c) 2008, 2026 Anthony Green SuperH SHmedia Foreign Function Interface diff --git a/deps/libffi/src/sparc/ffi.c b/deps/libffi/src/sparc/ffi.c index 98142b206ecd44..bcda1ff2219703 100644 --- a/deps/libffi/src/sparc/ffi.c +++ b/deps/libffi/src/sparc/ffi.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - ffi.c - Copyright (c) 2011, 2013 Anthony Green + ffi.c - Copyright (c) 2011, 2013, 2026 Anthony Green Copyright (c) 1996, 2003-2004, 2007-2008 Red Hat, Inc. SPARC Foreign Function Interface @@ -288,6 +288,7 @@ ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue, size_t bytes = cif->bytes; size_t i, nargs = cif->nargs; ffi_type **arg_types = cif->arg_types; + void **avalue_copy = NULL; FFI_ASSERT (cif->abi == FFI_V8); @@ -298,7 +299,8 @@ ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue, bytes += FFI_ALIGN (cif->rtype->size, 8); /* If we have any structure arguments, make a copy so we are passing - by value. */ + by value. The pointer array is cloned first: the caller owns avalue[] + and may reuse it for another call, so it must not be modified. */ for (i = 0; i < nargs; i++) { ffi_type *at = arg_types[i]; @@ -306,6 +308,12 @@ ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue, if (at->type == FFI_TYPE_STRUCT) { char *argcopy = alloca (size); + if (avalue_copy == NULL) + { + avalue_copy = alloca (nargs * sizeof (void *)); + memcpy (avalue_copy, avalue, nargs * sizeof (void *)); + avalue = avalue_copy; + } memcpy (argcopy, avalue[i], size); avalue[i] = argcopy; } diff --git a/deps/libffi/src/sparc/ffi64.c b/deps/libffi/src/sparc/ffi64.c index 09a5e59dfdd0c7..5eec1499e1708d 100644 --- a/deps/libffi/src/sparc/ffi64.c +++ b/deps/libffi/src/sparc/ffi64.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - ffi.c - Copyright (c) 2011, 2013 Anthony Green + ffi.c - Copyright (c) 2011, 2013, 2026 Anthony Green Copyright (c) 1996, 2003-2004, 2007-2008 Red Hat, Inc. SPARC Foreign Function Interface @@ -429,6 +429,7 @@ ffi_call_int(ffi_cif *cif, void (*fn)(void), void *rvalue, size_t bytes = cif->bytes; size_t i, nargs = cif->nargs; ffi_type **arg_types = cif->arg_types; + void **avalue_copy = NULL; FFI_ASSERT (cif->abi == FFI_V9); @@ -436,7 +437,8 @@ ffi_call_int(ffi_cif *cif, void (*fn)(void), void *rvalue, bytes += FFI_ALIGN (cif->rtype->size, 16); /* If we have any large structure arguments, make a copy so we are passing - by value. */ + by value. The pointer array is cloned first: the caller owns avalue[] + and may reuse it for another call, so it must not be modified. */ for (i = 0; i < nargs; i++) { ffi_type *at = arg_types[i]; @@ -444,10 +446,16 @@ ffi_call_int(ffi_cif *cif, void (*fn)(void), void *rvalue, if (at->type == FFI_TYPE_STRUCT && size > 4) { char *argcopy = alloca (size); + if (avalue_copy == NULL) + { + avalue_copy = alloca (nargs * sizeof (void *)); + memcpy (avalue_copy, avalue, nargs * sizeof (void *)); + avalue = avalue_copy; + } memcpy (argcopy, avalue[i], size); avalue[i] = argcopy; } - } + } ffi_call_v9(cif, fn, rvalue, avalue, -bytes, closure); } diff --git a/deps/libffi/src/tramp.c b/deps/libffi/src/tramp.c index 76a9dfe75c2c3b..525f8154715671 100644 --- a/deps/libffi/src/tramp.c +++ b/deps/libffi/src/tramp.c @@ -1,6 +1,6 @@ /* ----------------------------------------------------------------------- tramp.c - Copyright (c) 2020 Madhavan T. Venkataraman - Copyright (c) 2022 Anthony Green + Copyright (c) 2022, 2026 Anthony Green API and support functions for managing statically defined closure trampolines. @@ -201,11 +201,18 @@ static int tramp_table_alloc (void); #if defined (__linux__) || defined (__CYGWIN__) +/* Stringify a macro value for use in a scanf field-width specifier. */ +#define FFI_TRAMP_STR_(x) #x +#define FFI_TRAMP_STR(x) FFI_TRAMP_STR_(x) + static int ffi_tramp_get_libffi (void) { FILE *fp; - char file[PATH_MAX], line[PATH_MAX+100], perm[10], dev[10]; + /* `file' is sized PATH_MAX+1 so a scanf field width of PATH_MAX + (which permits PATH_MAX characters plus the terminating NUL) cannot + overflow. */ + char file[PATH_MAX+1], line[PATH_MAX+100], perm[10], dev[10]; unsigned long start, end, offset, inode; uintptr_t addr = (uintptr_t) tramp_globals.text; int nfields, found; @@ -215,7 +222,7 @@ ffi_tramp_get_libffi (void) open_flags |= O_CLOEXEC; #endif - snprintf (file, PATH_MAX, "/proc/%d/maps", getpid()); + snprintf (file, sizeof (file), "/proc/%d/maps", getpid()); fp = fopen (file, "r"); if (fp == NULL) return 0; @@ -225,7 +232,10 @@ ffi_tramp_get_libffi (void) if (fgets (line, sizeof (line), fp) == 0) break; - nfields = sscanf (line, "%lx-%lx %9s %lx %9s %ld %s", + /* Bound the path field width so a long mapping name cannot overflow + the fixed-size `file' buffer. */ + nfields = sscanf (line, + "%lx-%lx %9s %lx %9s %ld %" FFI_TRAMP_STR(PATH_MAX) "s", &start, &end, perm, &offset, dev, &inode, file); if (nfields != 7) continue; diff --git a/deps/libffi/src/types.c b/deps/libffi/src/types.c index c6e8ebe2a881be..4ac45a4ac0d790 100644 --- a/deps/libffi/src/types.c +++ b/deps/libffi/src/types.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - types.c - Copyright (c) 1996, 1998, 2024, 2025 Red Hat, Inc. + types.c - Copyright (c) 1996, 1998, 2024, 2025, 2026 Red Hat, Inc. Predefined ffi_types needed by libffi. diff --git a/deps/libffi/src/wasm/ffi.c b/deps/libffi/src/wasm/ffi.c index 486ffa7856aa82..b631b1b2bef9cf 100644 --- a/deps/libffi/src/wasm/ffi.c +++ b/deps/libffi/src/wasm/ffi.c @@ -1,5 +1,6 @@ /* ----------------------------------------------------------------------- ffi.c - Copyright (c) 2018-2023 Hood Chatham, Brion Vibber, Kleis Auke Wolthuizen, and others. + Copyright (c) 2026 Anthony Green wasm32/emscripten Foreign Function Interface @@ -70,6 +71,13 @@ EM_JS_DEPS(libffi, "$getWasmTableEntry,$setWasmTableEntry,$getEmptyTableSlot,$co #define DEREF_PTR(addr, offset) DEREF_U32(addr, offset) #define DEREF_PTR_NUMBER(addr, offset) DEREF_PTR(addr, offset) +// Store an integral return value widened to ffi_arg (32 bits on wasm32), as +// the libffi API promises for integral returns narrower than a register. +// The typed-array store wraps modulo 2^32, so a sign-extended negative +// Number is stored correctly by both variants. +#define STORE_ARG_WIDENED_UNSIGNED(rvalue, x) (DEREF_U32(rvalue, 0) = (x)) +#define STORE_ARG_WIDENED_SIGNED(rvalue, x) (DEREF_U32(rvalue, 0) = (x)) + CHECK_FIELD_OFFSET(ffi_cif, abi, 4*0); CHECK_FIELD_OFFSET(ffi_cif, nargs, 4*1); CHECK_FIELD_OFFSET(ffi_cif, arg_types, 4*2); @@ -109,6 +117,16 @@ CHECK_FIELD_OFFSET(ffi_type, elements, 8); #define DEREF_PTR(addr, offset) DEREF_U64(addr, offset) #define DEREF_PTR_NUMBER(addr, offset) DEC_PTR(DEREF_PTR(addr, offset)) +// Store an integral return value widened to ffi_arg (64 bits on wasm64), as +// the libffi API promises for integral returns narrower than a register. +// BigInt masking treats the value as infinite-precision two's complement, so +// the unsigned variant zero-extends a possibly-negative i32 Number, while +// the BigUint64Array store wraps the signed variant modulo 2^64, which is +// sign extension. +#define STORE_ARG_WIDENED_UNSIGNED(rvalue, x) \ + (DEREF_U64(rvalue, 0) = BigInt(x) & BigInt(4294967295)) +#define STORE_ARG_WIDENED_SIGNED(rvalue, x) (DEREF_U64(rvalue, 0) = BigInt(x)) + CHECK_FIELD_OFFSET(ffi_cif, abi, 0); CHECK_FIELD_OFFSET(ffi_cif, nargs, 4); CHECK_FIELD_OFFSET(ffi_cif, arg_types, 8); @@ -247,6 +265,10 @@ ffi_call_js, (ffi_cif *cif, ffi_fp fn, void *rvalue, void **avalue), var rtype_unboxed = unbox_small_structs(CIF__RTYPE(cif)); var rtype_ptr = rtype_unboxed[0]; var rtype_id = rtype_unboxed[1]; + // A small struct return is unboxed to a scalar type id above, but it is + // still a struct to the caller: its buffer is exactly the struct's size, + // so it must not get the integral widening to ffi_arg. + var rtype_widen = FFI_TYPE__TYPEID(DEC_PTR(CIF__RTYPE(cif))) !== FFI_TYPE_STRUCT; var orig_stack_ptr = stackSave(); var cur_stack_ptr = orig_stack_ptr; @@ -267,6 +289,15 @@ ffi_call_js, (ffi_cif *cif, ffi_fp fn, void *rvalue, void **avalue), // just use this. We also mark a flag that we don't need to convert the return // value of the dynamic call back to C. if (rtype_id === FFI_TYPE_LONGDOUBLE || rtype_id === FFI_TYPE_STRUCT) { + if (rvalue === 0) { + // NULL rvalue: the caller discards the result, but the callee still + // needs somewhere to write it. Allocate scratch space on the stack; + // it is released by the stackRestore after the call. + var rsize = DEC_PTR(FFI_TYPE__SIZE(rtype_ptr)); + var ralign = FFI_TYPE__ALIGN(rtype_ptr); + STACK_ALLOC(cur_stack_ptr, rsize, ralign); + rvalue = cur_stack_ptr; + } args.push(ENC_PTR(rvalue)); ret_by_arg = true; } @@ -430,15 +461,31 @@ ffi_call_js, (ffi_cif *cif, ffi_fp fn, void *rvalue, void **avalue), return; } + // A NULL rvalue means the caller discards the return value. + if (rvalue === 0) { + return; + } + // Otherwise the result was automatically converted from C into Javascript and // we need to manually convert it back to C. + // Integral returns narrower than a register are widened to ffi_arg, as + // documented and as every native port does -- but only genuine scalar + // returns: an unboxed small-struct return keeps its natural width. switch (rtype_id) { case FFI_TYPE_VOID: break; case FFI_TYPE_INT: - case FFI_TYPE_UINT32: case FFI_TYPE_SINT32: - DEREF_U32(rvalue, 0) = result; + if (rtype_widen) + STORE_ARG_WIDENED_SIGNED(rvalue, result | 0); + else + DEREF_U32(rvalue, 0) = result; + break; + case FFI_TYPE_UINT32: + if (rtype_widen) + STORE_ARG_WIDENED_UNSIGNED(rvalue, result | 0); + else + DEREF_U32(rvalue, 0) = result; break; case FFI_TYPE_FLOAT: DEREF_F32(rvalue, 0) = result; @@ -447,12 +494,28 @@ ffi_call_js, (ffi_cif *cif, ffi_fp fn, void *rvalue, void **avalue), DEREF_F64(rvalue, 0) = result; break; case FFI_TYPE_UINT8: + if (rtype_widen) + STORE_ARG_WIDENED_UNSIGNED(rvalue, result & 0xff); + else + DEREF_U8(rvalue, 0) = result; + break; case FFI_TYPE_SINT8: - DEREF_U8(rvalue, 0) = result; + if (rtype_widen) + STORE_ARG_WIDENED_SIGNED(rvalue, (result << 24) >> 24); + else + DEREF_U8(rvalue, 0) = result; break; case FFI_TYPE_UINT16: + if (rtype_widen) + STORE_ARG_WIDENED_UNSIGNED(rvalue, result & 0xffff); + else + DEREF_U16(rvalue, 0) = result; + break; case FFI_TYPE_SINT16: - DEREF_U16(rvalue, 0) = result; + if (rtype_widen) + STORE_ARG_WIDENED_SIGNED(rvalue, (result << 16) >> 16); + else + DEREF_U16(rvalue, 0) = result; break; case FFI_TYPE_UINT64: case FFI_TYPE_SINT64: diff --git a/deps/libffi/src/x86/ffi.c b/deps/libffi/src/x86/ffi.c index 40d153fb399e21..27f17b0c884957 100644 --- a/deps/libffi/src/x86/ffi.c +++ b/deps/libffi/src/x86/ffi.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - ffi.c - Copyright (c) 2017, 2022 Anthony Green + ffi.c - Copyright (c) 2017, 2022, 2026 Anthony Green Copyright (c) 1996, 1998, 1999, 2001, 2007, 2008 Red Hat, Inc. Copyright (c) 2002 Ranjit Mathew Copyright (c) 2002 Bo Thorsen @@ -554,8 +554,14 @@ ffi_closure_inner (struct closure_frame *frame, char *stack) return flags | (cif->bytes << X86_RET_POP_SHIFT); case FFI_THISCALL: case FFI_FASTCALL: - return flags | ((cif->bytes - (narg_reg * FFI_SIZEOF_ARG)) - << X86_RET_POP_SHIFT); + /* The callee must pop exactly the bytes that were passed on the + stack. Deriving that from cif->bytes minus narg_reg * 4 is wrong + once narg_reg has been force-bumped to 2 (above) for a 64-bit or + struct argument that is itself placed on the stack: the subtraction + then discounts register slots that were never used, under-popping + the stack. argp has advanced past exactly the stack-resident + arguments (dir == 1 for these ABIs), so use that directly. */ + return flags | (((unsigned) (argp - stack)) << X86_RET_POP_SHIFT); default: return flags; } diff --git a/deps/libffi/src/x86/ffi64.c b/deps/libffi/src/x86/ffi64.c index db0aa563a87947..c24db38c4364b7 100644 --- a/deps/libffi/src/x86/ffi64.c +++ b/deps/libffi/src/x86/ffi64.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - ffi64.c - Copyright (c) 2011, 2018, 2022 Anthony Green + ffi64.c - Copyright (c) 2011, 2018, 2022, 2026 Anthony Green Copyright (c) 2013 The Written Word, Inc. Copyright (c) 2008, 2010 Red Hat, Inc. Copyright (c) 2002, 2007 Bo Thorsen @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #include "internal64.h" @@ -734,6 +736,340 @@ ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue, } #ifndef __ILP32__ +/* ===================================================================== + Precompiled argument-placement plan, used by the ffi_call_plan API. + + ffi_prep_cif_machdep classifies the signature once, but ffi_call_int then + re-derives the same per-argument placement on every call (~650 instructions + for a 3-argument call). A "plan" captures that placement as a flat move + list, built once, so the register_args + stack buffer can be filled with no + re-classification before handing off to the unchanged ffi_call_unix64. For + the common case (only 64-bit GP arguments) a direct thunk loads the values + straight into the argument registers, skipping the buffer entirely. + + A plan is built by ffi_call_plan_alloc and applied by ffi_call_plan_invoke; + the caller owns it and reuses it across calls. + + Scalar, pointer, int128, float and double arguments are handled; any struct, + complex, or x87 long double argument has no plan, so the caller's invoke + falls back to ffi_call. */ + +enum ffi_move_op +{ + FFI_MOVE_SE8, FFI_MOVE_SE16, FFI_MOVE_SE32, /* sign-extend N bytes -> gpr */ + FFI_MOVE_GP64, /* copy a full 8-byte word -> gpr */ + FFI_MOVE_GP, /* zero gpr, copy len(<8) bytes */ + FFI_MOVE_SSE64, FFI_MOVE_SSE32, /* copy 8/4 bytes -> sse slot */ + FFI_MOVE_STACK /* copy len bytes -> stack */ +}; + +typedef struct +{ + unsigned src_idx; /* avalue[] index */ + unsigned src_off; /* byte offset within avalue[src_idx] (chunk * 8) */ + unsigned dst_off; /* byte offset within the register_args+stack buf */ + unsigned len; /* bytes for FFI_MOVE_GP / FFI_MOVE_STACK */ + unsigned char op; +} ffi_move; + +typedef struct +{ + unsigned nmoves; + unsigned ssecount; /* -> reg_args->rax */ + unsigned bytes; /* stack-arg area size (== cif->bytes) */ + unsigned flags; /* == cif->flags */ + unsigned ret_in_mem; /* nonzero -> reg_args->gpr[0] = rvalue */ + unsigned fast; /* nonzero -> lean trampoline eligible */ + unsigned retcode; /* UNIX64_RET_* (low byte of flags) for the store */ + int thunk_n; /* >=0 -> ffi_gp_thunks[thunk_n], else -1 */ + ffi_move moves[]; +} ffi_plan; + +/* Return of the lean trampoline / direct thunks: callee's rax in .i, xmm0 in .d. */ +struct ffi_ret2 { UINT64 i; double d; }; +extern struct ffi_ret2 ffi_plan_fast_call (struct register_args *img, + void (*fn) (void)) FFI_HIDDEN; + +/* Count-based direct thunks: load avalue[0..N-1] into arg registers, call. */ +extern struct ffi_ret2 ffi_plan_gp0 (void **, void (*)(void)) FFI_HIDDEN; +extern struct ffi_ret2 ffi_plan_gp1 (void **, void (*)(void)) FFI_HIDDEN; +extern struct ffi_ret2 ffi_plan_gp2 (void **, void (*)(void)) FFI_HIDDEN; +extern struct ffi_ret2 ffi_plan_gp3 (void **, void (*)(void)) FFI_HIDDEN; +extern struct ffi_ret2 ffi_plan_gp4 (void **, void (*)(void)) FFI_HIDDEN; +extern struct ffi_ret2 ffi_plan_gp5 (void **, void (*)(void)) FFI_HIDDEN; +extern struct ffi_ret2 ffi_plan_gp6 (void **, void (*)(void)) FFI_HIDDEN; +static struct ffi_ret2 (*const ffi_gp_thunks[7]) (void **, void (*)(void)) = + { ffi_plan_gp0, ffi_plan_gp1, ffi_plan_gp2, ffi_plan_gp3, + ffi_plan_gp4, ffi_plan_gp5, ffi_plan_gp6 }; + +/* Store the callee return value, replicating the unix64.S store_table widths. */ +static inline void +store_ret (void *rvalue, unsigned retcode, struct ffi_ret2 r) +{ + switch (retcode) + { + case UNIX64_RET_VOID: break; + case UNIX64_RET_UINT8: *(UINT64 *) rvalue = (UINT8) r.i; break; + case UNIX64_RET_UINT16: *(UINT64 *) rvalue = (UINT16) r.i; break; + case UNIX64_RET_UINT32: *(UINT64 *) rvalue = (UINT32) r.i; break; + case UNIX64_RET_SINT8: *(UINT64 *) rvalue = (UINT64)(SINT64)(SINT8) r.i; break; + case UNIX64_RET_SINT16: *(UINT64 *) rvalue = (UINT64)(SINT64)(SINT16) r.i; break; + case UNIX64_RET_SINT32: *(UINT64 *) rvalue = (UINT64)(SINT64)(SINT32) r.i; break; + case UNIX64_RET_INT64: *(UINT64 *) rvalue = r.i; break; + case UNIX64_RET_XMM32: memcpy (rvalue, &r.d, 4); break; + case UNIX64_RET_XMM64: memcpy (rvalue, &r.d, 8); break; + } +} + +/* Build the move-list for CIF, or NULL if not plan-able (caller falls back). */ +static ffi_plan * +build_plan (ffi_cif *cif) +{ + unsigned i, avn = cif->nargs; + enum x86_64_reg_class classes[MAX_CLASSES]; + unsigned nm, gprcount, ssecount; + size_t argp_off; + ffi_plan *plan; + int all_gp64 = 1; /* every arg is exactly one 64-bit GP move? */ + + if (cif->abi != FFI_UNIX64) + return NULL; + + /* Reject arg types this cut doesn't encode; returns are handled by flags. */ + for (i = 0; i < avn; i++) + { + int t = cif->arg_types[i]->type; + if (t == FFI_TYPE_STRUCT || t == FFI_TYPE_COMPLEX) + return NULL; +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + if (t == FFI_TYPE_LONGDOUBLE) + return NULL; +#endif + } + + /* One self-contained allocation: header + moves, released with plain free(). */ + plan = malloc (sizeof (ffi_plan) + sizeof (ffi_move) * (2 * avn + 1)); + if (plan == NULL) + return NULL; + + nm = gprcount = ssecount = 0; + argp_off = 0; + plan->ret_in_mem = (cif->flags & UNIX64_FLAG_RET_IN_MEM) ? 1 : 0; + if (plan->ret_in_mem) + gprcount++; /* sret pointer occupies gpr[0] */ + + for (i = 0; i < avn; i++) + { + ffi_type *at = cif->arg_types[i]; + size_t size = at->size, n, rem; + int ngpr, nsse; + unsigned j; + + n = examine_argument (at, classes, 0, &ngpr, &nsse); + if (n == 0 + || gprcount + ngpr > MAX_GPR_REGS + || ssecount + nsse > MAX_SSE_REGS) + { + long align = at->alignment; + ffi_move *m = &plan->moves[nm++]; + all_gp64 = 0; + if (align < 8) + align = 8; + argp_off = FFI_ALIGN (argp_off, align); + m->op = FFI_MOVE_STACK; + m->src_idx = i; + m->src_off = 0; + m->dst_off = (unsigned) (sizeof (struct register_args) + argp_off); + m->len = (unsigned) size; + argp_off += size; + continue; + } + + for (j = 0, rem = size; j < n; j++, rem -= 8) + { + ffi_move m; + m.src_idx = i; + m.src_off = j * 8; + switch (classes[j]) + { + case X86_64_NO_CLASS: + case X86_64_SSEUP_CLASS: + continue; /* nothing placed for this 8-byte */ + case X86_64_INTEGER_CLASS: + case X86_64_INTEGERSI_CLASS: + m.dst_off = gprcount * 8; /* offsetof(register_args,gpr) == 0 */ + switch (at->type) + { + case FFI_TYPE_SINT8: m.op = FFI_MOVE_SE8; all_gp64 = 0; break; + case FFI_TYPE_SINT16: m.op = FFI_MOVE_SE16; all_gp64 = 0; break; + case FFI_TYPE_SINT32: m.op = FFI_MOVE_SE32; all_gp64 = 0; break; + default: + if (rem >= 8) + m.op = FFI_MOVE_GP64; + else + { m.op = FFI_MOVE_GP; m.len = (unsigned) rem; all_gp64 = 0; } + break; + } + gprcount++; + break; + case X86_64_SSE_CLASS: + case X86_64_SSEDF_CLASS: + m.dst_off = (unsigned) (offsetof (struct register_args, sse) + + ssecount * sizeof (union big_int_union)); + m.op = FFI_MOVE_SSE64; + ssecount++; + all_gp64 = 0; + break; + case X86_64_SSESF_CLASS: + m.dst_off = (unsigned) (offsetof (struct register_args, sse) + + ssecount * sizeof (union big_int_union)); + m.op = FFI_MOVE_SSE32; + ssecount++; + all_gp64 = 0; + break; + default: + free (plan); /* X87 etc. in registers: bail */ + return NULL; + } + plan->moves[nm++] = m; + } + } + + plan->nmoves = nm; + plan->ssecount = ssecount; + plan->bytes = cif->bytes; + plan->flags = cif->flags; + plan->retcode = cif->flags & 0xff; /* UNIX64_RET_* */ + /* Lean-trampoline eligible: no spilled stack args and a simple return + (VOID..XMM64, codes 0..9; RET_IN_MEM has low byte VOID). Struct-in-regs + (>=12) and x87 (10,11) returns stay on ffi_call_unix64. */ + plan->fast = (cif->bytes == 0 && plan->retcode <= UNIX64_RET_XMM64) ? 1 : 0; + /* Pure-GP64 direct thunk: every arg is one 64-bit GP value (so a plain load + per arg is exact), <=6 of them, no sret, simple return -> load avalue + straight into the arg registers, no register image. */ + plan->thunk_n = + (all_gp64 && !plan->ret_in_mem && nm == avn && avn <= MAX_GPR_REGS + && plan->fast) + ? (int) avn : -1; + return plan; +} + +/* Execute PLAN: rebuild register_args + stack buffer, then ffi_call_unix64. */ +FFI_ASAN_NO_SANITIZE +static inline __attribute__ ((always_inline)) void +plan_exec (ffi_cif *cif, ffi_plan *plan, void (*fn) (void), + void *rvalue, void **avalue) +{ + unsigned flags = plan->flags; + struct register_args local __attribute__ ((aligned (16))); + char *stack = NULL; + struct register_args *reg_args; + unsigned k; + + if (rvalue == NULL) + { + if (flags & UNIX64_FLAG_RET_IN_MEM) + rvalue = alloca (cif->rtype->size); + else + flags = UNIX64_RET_VOID; + } + + if (plan->thunk_n >= 0) + { + /* Pure-GP64: load avalue straight into arg regs, no image at all. */ + struct ffi_ret2 r = ffi_gp_thunks[plan->thunk_n] (avalue, fn); + if (rvalue != NULL) + store_ret (rvalue, plan->retcode, r); + return; + } + + if (plan->fast) + reg_args = &local; /* no stack args: fixed local image */ + else + { + stack = alloca (sizeof (struct register_args) + plan->bytes + 4 * 8); + reg_args = (struct register_args *) stack; + } + reg_args->r10 = 0; /* closure (none for ffi_call) */ + if (plan->ret_in_mem) + reg_args->gpr[0] = (UINT64) (uintptr_t) rvalue; + + for (k = 0; k < plan->nmoves; k++) + { + ffi_move *m = &plan->moves[k]; + char *src = (char *) avalue[m->src_idx] + m->src_off; + char *dst = (char *) reg_args + m->dst_off; + switch (m->op) + { + /* x86-64: unaligned scalar loads from avalue[] are fine. */ + case FFI_MOVE_SE8: *(UINT64 *) dst = (UINT64) (SINT64) *(SINT8 *) src; break; + case FFI_MOVE_SE16: *(UINT64 *) dst = (UINT64) (SINT64) *(SINT16 *) src; break; + case FFI_MOVE_SE32: *(UINT64 *) dst = (UINT64) (SINT64) *(SINT32 *) src; break; + case FFI_MOVE_GP64: *(UINT64 *) dst = *(UINT64 *) src; break; + case FFI_MOVE_GP: *(UINT64 *) dst = 0; memcpy (dst, src, m->len); break; + case FFI_MOVE_SSE64: *(UINT64 *) dst = *(UINT64 *) src; break; + case FFI_MOVE_SSE32: *(UINT32 *) dst = *(UINT32 *) src; break; + case FFI_MOVE_STACK: memcpy (dst, src, m->len); break; + } + } + reg_args->rax = plan->ssecount; + + if (plan->fast) + { + /* No stack args; lean trampoline + return store replicating the + unix64.S store_table widths. ret_in_mem already wrote gpr[0]. */ + struct ffi_ret2 r = ffi_plan_fast_call (reg_args, fn); + if (rvalue != NULL) + store_ret (rvalue, plan->retcode, r); + return; + } + + ffi_call_unix64 (stack, plan->bytes + sizeof (struct register_args), + flags, rvalue, fn); +} + +/* Reusable call plan: an opaque, caller-owned handle wrapping a prebuilt plan. + ffi_call_plan_invoke applies it directly, skipping the per-call argument + classification ffi_call does every time. Signatures with no fast path + (FAST is NULL) fall back to ffi_call. The plan is immutable after alloc, so + it carries no per-thread state and can be invoked from any thread. */ +struct ffi_call_plan +{ + ffi_cif *cif; + ffi_plan *fast; /* prebuilt plan, or NULL -> fall back to ffi_call */ +}; + +ffi_call_plan * +ffi_call_plan_alloc (ffi_cif *cif) +{ + ffi_call_plan *plan = malloc (sizeof (struct ffi_call_plan)); + if (plan == NULL) + return NULL; + plan->cif = cif; + plan->fast = build_plan (cif); /* NULL if this signature has no fast path */ + return plan; +} + +void +ffi_call_plan_invoke (ffi_call_plan *plan, void (*fn) (void), + void *rvalue, void **avalue) +{ + if (plan->fast != NULL) + plan_exec (plan->cif, plan->fast, fn, rvalue, avalue); + else + ffi_call (plan->cif, fn, rvalue, avalue); +} + +void +ffi_call_plan_free (ffi_call_plan *plan) +{ + if (plan != NULL) + { + free (plan->fast); + free (plan); + } +} + extern void ffi_call_efi64(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue); #endif @@ -742,11 +1078,13 @@ void ffi_call (ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) { ffi_type **arg_types = cif->arg_types; + void **avalue_copy = NULL; int i, nargs = cif->nargs; const int max_reg_struct_size = cif->abi == FFI_GNUW64 ? 8 : 16; /* If we have any large structure arguments, make a copy so we are passing - by value. */ + by value. The pointer array is cloned first: the caller owns avalue[] + and may reuse it for another call, so it must not be modified. */ for (i = 0; i < nargs; i++) { ffi_type *at = arg_types[i]; @@ -754,6 +1092,12 @@ ffi_call (ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) if (at->type == FFI_TYPE_STRUCT && size > max_reg_struct_size) { char *argcopy = alloca (size); + if (avalue_copy == NULL) + { + avalue_copy = alloca (nargs * sizeof (void *)); + memcpy (avalue_copy, avalue, nargs * sizeof (void *)); + avalue = avalue_copy; + } memcpy (argcopy, avalue[i], size); avalue[i] = argcopy; } @@ -884,6 +1228,7 @@ ffi_closure_unix64_inner(ffi_cif *cif, avn = cif->nargs; flags = cif->flags; + avalue = alloca(avn * sizeof(void *)); gprcount = ssecount = 0; @@ -940,7 +1285,7 @@ ffi_closure_unix64_inner(ffi_cif *cif, /* Otherwise, allocate space to make them consecutive. */ else { - char *a = alloca (16); + char *a = alloca (n * 8); unsigned int j; avalue[i] = a; diff --git a/deps/libffi/src/x86/ffitarget.h b/deps/libffi/src/x86/ffitarget.h index 995469b0f59b2c..d702235f90fed5 100644 --- a/deps/libffi/src/x86/ffitarget.h +++ b/deps/libffi/src/x86/ffitarget.h @@ -1,5 +1,5 @@ /* -----------------------------------------------------------------*-C-*- - ffitarget.h - Copyright (c) 2012, 2014, 2018 Anthony Green + ffitarget.h - Copyright (c) 2012, 2014, 2018, 2026 Anthony Green Copyright (c) 1996-2003, 2010 Red Hat, Inc. Copyright (C) 2008 Free Software Foundation, Inc. diff --git a/deps/libffi/src/x86/ffiw64.c b/deps/libffi/src/x86/ffiw64.c index 37d9a7e9c036d9..f8db4e0d96236b 100644 --- a/deps/libffi/src/x86/ffiw64.c +++ b/deps/libffi/src/x86/ffiw64.c @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - ffiw64.c - Copyright (c) 2018 Anthony Green + ffiw64.c - Copyright (c) 2018, 2026 Anthony Green Copyright (c) 2014 Red Hat, Inc. x86 win64 Foreign Function Interface @@ -126,12 +126,15 @@ ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue, size_t rsize; struct win64_call_frame *frame; ffi_type **arg_types = cif->arg_types; + void **avalue_copy = NULL; int nargs = cif->nargs; FFI_ASSERT(cif->abi == FFI_GNUW64 || cif->abi == FFI_WIN64); /* If we have any int128 or irregularly sized structure arguments, - make a copy so we are passing by value. */ + make a copy so we are passing by value. The pointer array is cloned + first: the caller owns avalue[] and may reuse it for another call, + so it must not be modified. */ for (i = 0; i < nargs; i++) { ffi_type *at = arg_types[i]; @@ -159,6 +162,12 @@ ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue, if (needcopy) { char *argcopy = alloca (size); + if (avalue_copy == NULL) + { + avalue_copy = alloca (nargs * sizeof (void *)); + memcpy (avalue_copy, avalue, nargs * sizeof (void *)); + avalue = avalue_copy; + } memcpy (argcopy, avalue[i], size); avalue[i] = argcopy; } diff --git a/deps/libffi/src/x86/sysv.S b/deps/libffi/src/x86/sysv.S index b5969df1560143..dac841421d9b2b 100644 --- a/deps/libffi/src/x86/sysv.S +++ b/deps/libffi/src/x86/sysv.S @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - sysv.S - Copyright (c) 2017 Anthony Green + sysv.S - Copyright (c) 2017, 2026 Anthony Green - Copyright (c) 2013 The Written Word, Inc. - Copyright (c) 1996,1998,2001-2003,2005,2008,2010 Red Hat, Inc. @@ -1269,6 +1269,6 @@ L(EFDE9): #endif /* ifdef __i386__ */ -#if defined __ELF__ && defined __linux__ +#if defined __ELF__ && (defined __linux__ || defined __FreeBSD__) .section .note.GNU-stack,"",@progbits #endif diff --git a/deps/libffi/src/x86/sysv_intel.S b/deps/libffi/src/x86/sysv_intel.S index efdde14ac20290..c7de159916ac9c 100644 --- a/deps/libffi/src/x86/sysv_intel.S +++ b/deps/libffi/src/x86/sysv_intel.S @@ -1,5 +1,5 @@ /* ----------------------------------------------------------------------- - sysv.S - Copyright (c) 2017, 2022 Anthony Green + sysv.S - Copyright (c) 2017, 2022, 2026 Anthony Green - Copyright (c) 2013 The Written Word, Inc. - Copyright (c) 1996,1998,2001-2003,2005,2008,2010 Red Hat, Inc. @@ -992,7 +992,7 @@ L(EFDE9): #endif /* ifndef _MSC_VER */ #endif /* ifndef __x86_64__ */ -#if defined __ELF__ && defined __linux__ +#if defined __ELF__ && (defined __linux__ || defined __FreeBSD__) .section .note.GNU-stack,"",@progbits #endif #endif diff --git a/deps/libffi/src/x86/unix64.S b/deps/libffi/src/x86/unix64.S index da30ab7577a7c3..61983ef3bcf5ec 100644 --- a/deps/libffi/src/x86/unix64.S +++ b/deps/libffi/src/x86/unix64.S @@ -2,6 +2,7 @@ unix64.S - Copyright (c) 2013 The Written Word, Inc. - Copyright (c) 2008 Red Hat, Inc - Copyright (c) 2002 Bo Thorsen + - Copyright (c) 2026 Anthony Green x86-64 Foreign Function Interface @@ -242,6 +243,154 @@ L(load_sse): L(UW4): ENDF(C(ffi_call_unix64)) +/* Lean trampoline for the plan fast path: no stack args, simple return. + struct { UINT64 rax; double xmm0; } + ffi_plan_fast_call (struct register_args *img /rdi/, void (*fn)(void) /rsi/); + + Loads the argument registers from IMG, calls FN, and lets the callee's + rax/xmm0 flow straight out as this function's {UINT64,double} return -- so + the C caller recovers both with no return-dispatch table. Skips the frame + relocation that ffi_call_unix64 needs only for stack args and struct/x87 + returns. Caller guarantees img has no spilled stack arguments. */ + .balign 8 + .globl C(ffi_plan_fast_call) + FFI_HIDDEN(C(ffi_plan_fast_call)) +C(ffi_plan_fast_call): + .cfi_startproc + _CET_ENDBR + movq %rsi, %r11 /* fn */ + movq %rdi, %rax /* img */ + movl 0xb0(%rax), %r10d /* ssecount */ + testl %r10d, %r10d + jz 1f + movdqa 0x30(%rax), %xmm0 + movdqa 0x40(%rax), %xmm1 + movdqa 0x50(%rax), %xmm2 + movdqa 0x60(%rax), %xmm3 + movdqa 0x70(%rax), %xmm4 + movdqa 0x80(%rax), %xmm5 + movdqa 0x90(%rax), %xmm6 + movdqa 0xa0(%rax), %xmm7 +1: + movq 0x00(%rax), %rdi + movq 0x08(%rax), %rsi + movq 0x10(%rax), %rdx + movq 0x18(%rax), %rcx + movq 0x20(%rax), %r8 + movq 0x28(%rax), %r9 + movl %r10d, %eax /* %al = ssecount */ + subq $8, %rsp /* realign to 16 across the call */ + .cfi_adjust_cfa_offset 8 + call *%r11 + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + ret /* rax + xmm0 carry the callee's return */ + .cfi_endproc + ENDF(C(ffi_plan_fast_call)) + +/* Count-based direct thunks for the pure-GP64 fast path: load avalue[0..N-1] + straight into the argument registers (no register_args image) and call. + struct { UINT64 rax; double xmm0; } + ffi_plan_gpN (void **avalue /rdi/, void (*fn)(void) /rsi/); + Eligible only when every arg is a single 64-bit GP value (no sign-extension, + no SSE, no stack spill, no sret) -- so a plain 8-byte load per arg is exact. + rax/xmm0 flow out as the {UINT64,double} return; C stores per return code. */ + +#define FFI_GP_HEAD \ + .cfi_startproc; \ + _CET_ENDBR; \ + movq %rsi, %r11; \ + movq %rdi, %rax +#define FFI_GP_LD(OFS, REG) \ + movq OFS(%rax), %r10; \ + movq (%r10), REG +#define FFI_GP_TAIL \ + xorl %eax, %eax; \ + subq $8, %rsp; \ + .cfi_adjust_cfa_offset 8; \ + call *%r11; \ + addq $8, %rsp; \ + .cfi_adjust_cfa_offset -8; \ + ret; \ + .cfi_endproc + + .balign 8 + .globl C(ffi_plan_gp0) + FFI_HIDDEN(C(ffi_plan_gp0)) +C(ffi_plan_gp0): + FFI_GP_HEAD + FFI_GP_TAIL + ENDF(C(ffi_plan_gp0)) + + .balign 8 + .globl C(ffi_plan_gp1) + FFI_HIDDEN(C(ffi_plan_gp1)) +C(ffi_plan_gp1): + FFI_GP_HEAD + FFI_GP_LD(0x00, %rdi) + FFI_GP_TAIL + ENDF(C(ffi_plan_gp1)) + + .balign 8 + .globl C(ffi_plan_gp2) + FFI_HIDDEN(C(ffi_plan_gp2)) +C(ffi_plan_gp2): + FFI_GP_HEAD + FFI_GP_LD(0x08, %rsi) + FFI_GP_LD(0x00, %rdi) + FFI_GP_TAIL + ENDF(C(ffi_plan_gp2)) + + .balign 8 + .globl C(ffi_plan_gp3) + FFI_HIDDEN(C(ffi_plan_gp3)) +C(ffi_plan_gp3): + FFI_GP_HEAD + FFI_GP_LD(0x08, %rsi) + FFI_GP_LD(0x10, %rdx) + FFI_GP_LD(0x00, %rdi) + FFI_GP_TAIL + ENDF(C(ffi_plan_gp3)) + + .balign 8 + .globl C(ffi_plan_gp4) + FFI_HIDDEN(C(ffi_plan_gp4)) +C(ffi_plan_gp4): + FFI_GP_HEAD + FFI_GP_LD(0x08, %rsi) + FFI_GP_LD(0x10, %rdx) + FFI_GP_LD(0x18, %rcx) + FFI_GP_LD(0x00, %rdi) + FFI_GP_TAIL + ENDF(C(ffi_plan_gp4)) + + .balign 8 + .globl C(ffi_plan_gp5) + FFI_HIDDEN(C(ffi_plan_gp5)) +C(ffi_plan_gp5): + FFI_GP_HEAD + FFI_GP_LD(0x08, %rsi) + FFI_GP_LD(0x10, %rdx) + FFI_GP_LD(0x18, %rcx) + FFI_GP_LD(0x20, %r8) + FFI_GP_LD(0x00, %rdi) + FFI_GP_TAIL + ENDF(C(ffi_plan_gp5)) + + .balign 8 + .globl C(ffi_plan_gp6) + FFI_HIDDEN(C(ffi_plan_gp6)) +C(ffi_plan_gp6): + FFI_GP_HEAD + FFI_GP_LD(0x08, %rsi) + FFI_GP_LD(0x10, %rdx) + FFI_GP_LD(0x18, %rcx) + FFI_GP_LD(0x20, %r8) + FFI_GP_LD(0x28, %r9) + FFI_GP_LD(0x00, %rdi) + FFI_GP_TAIL + ENDF(C(ffi_plan_gp6)) + /* 6 general registers, 8 vector registers, 32 bytes of rvalue, 8 bytes of alignment. */ #define ffi_closure_OFS_G 0 @@ -724,6 +873,6 @@ L(EFDE5): #endif #endif /* __x86_64__ */ -#if defined __ELF__ && defined __linux__ +#if defined __ELF__ && (defined __linux__ || defined __FreeBSD__) .section .note.GNU-stack,"",@progbits #endif diff --git a/deps/libffi/src/x86/win64.S b/deps/libffi/src/x86/win64.S index e994fa9e1eea62..185f0a3048fba7 100644 --- a/deps/libffi/src/x86/win64.S +++ b/deps/libffi/src/x86/win64.S @@ -255,6 +255,6 @@ C(ffi_closure_win64_alt): #endif #endif /* __x86_64__ */ -#if defined __ELF__ && defined __linux__ +#if defined __ELF__ && (defined __linux__ || defined __FreeBSD__) .section .note.GNU-stack,"",@progbits #endif diff --git a/deps/libffi/src/x86/win64_intel.S b/deps/libffi/src/x86/win64_intel.S index b278a7440040d4..e9eff00da3ce6d 100644 --- a/deps/libffi/src/x86/win64_intel.S +++ b/deps/libffi/src/x86/win64_intel.S @@ -237,7 +237,7 @@ ffi_closure_win64_2 LABEL near cfi_endproc C(ffi_closure_win64) endp -#if defined __ELF__ && defined __linux__ +#if defined __ELF__ && (defined __linux__ || defined __FreeBSD__) .section .note.GNU-stack,"",@progbits #endif _text ends diff --git a/deps/libffi/testsuite/Makefile.am b/deps/libffi/testsuite/Makefile.am index 2e422b9a44e8e0..c14a880959d835 100644 --- a/deps/libffi/testsuite/Makefile.am +++ b/deps/libffi/testsuite/Makefile.am @@ -15,9 +15,13 @@ EXTRA_DIST = config/default.exp emscripten/build.sh emscripten/conftest.py \ libffi.call/align_stdcall.c libffi.call/bpo_38748.c libffi.call/call.exp \ libffi.call/err_bad_typedef.c libffi.call/ffitest.h libffi.call/float.c \ libffi.call/float1.c libffi.call/float2.c libffi.call/float3.c \ - libffi.call/float4.c libffi.call/float_va.c libffi.call/many.c \ + libffi.call/float4.c libffi.call/float_va.c libffi.call/i128-1.c \ + libffi.call/large_struct_by_value.c libffi.call/many.c \ libffi.call/many2.c libffi.call/many_double.c libffi.call/many_mixed.c \ + libffi.call/many_small_structs.c \ libffi.call/negint.c libffi.call/offsets.c libffi.call/overread.c \ + libffi.call/plan.c libffi.call/plan_mixed.c libffi.call/plan_spill.c \ + libffi.call/plan_struct.c libffi.call/plan_var.c \ libffi.call/pr1172638.c libffi.call/promotion.c libffi.call/pyobjc_tc.c libffi.call/return_dbl.c \ libffi.call/return_dbl1.c libffi.call/return_dbl2.c libffi.call/return_fl.c \ libffi.call/return_fl1.c libffi.call/return_fl2.c libffi.call/return_fl3.c \ @@ -77,7 +81,7 @@ EXTRA_DIST = config/default.exp emscripten/build.sh emscripten/conftest.py \ libffi.complex/cls_complex_va_double.c libffi.complex/cls_complex_va_float.c libffi.complex/cls_complex_va_longdouble.c \ libffi.complex/complex.exp libffi.complex/complex.inc libffi.complex/complex_defs_double.inc \ libffi.complex/complex_defs_float.inc libffi.complex/complex_defs_longdouble.inc libffi.complex/complex_double.c \ - libffi.complex/complex_float.c libffi.complex/complex_int.c libffi.complex/complex_longdouble.c \ + libffi.complex/complex_float.c libffi.complex/complex_i128.c libffi.complex/complex_int.c libffi.complex/complex_longdouble.c \ libffi.complex/ffitest.h libffi.complex/many_complex.inc libffi.complex/many_complex_double.c \ libffi.complex/many_complex_float.c libffi.complex/many_complex_longdouble.c libffi.complex/return_complex.inc \ libffi.complex/return_complex1.inc libffi.complex/return_complex1_double.c libffi.complex/return_complex1_float.c \ diff --git a/deps/libffi/testsuite/Makefile.in b/deps/libffi/testsuite/Makefile.in index 04f864ca251555..1b29b90d363346 100644 --- a/deps/libffi/testsuite/Makefile.in +++ b/deps/libffi/testsuite/Makefile.in @@ -303,9 +303,13 @@ EXTRA_DIST = config/default.exp emscripten/build.sh emscripten/conftest.py \ libffi.call/align_stdcall.c libffi.call/bpo_38748.c libffi.call/call.exp \ libffi.call/err_bad_typedef.c libffi.call/ffitest.h libffi.call/float.c \ libffi.call/float1.c libffi.call/float2.c libffi.call/float3.c \ - libffi.call/float4.c libffi.call/float_va.c libffi.call/many.c \ + libffi.call/float4.c libffi.call/float_va.c libffi.call/i128-1.c \ + libffi.call/large_struct_by_value.c libffi.call/many.c \ libffi.call/many2.c libffi.call/many_double.c libffi.call/many_mixed.c \ + libffi.call/many_small_structs.c \ libffi.call/negint.c libffi.call/offsets.c libffi.call/overread.c \ + libffi.call/plan.c libffi.call/plan_mixed.c libffi.call/plan_spill.c \ + libffi.call/plan_struct.c libffi.call/plan_var.c \ libffi.call/pr1172638.c libffi.call/promotion.c libffi.call/pyobjc_tc.c libffi.call/return_dbl.c \ libffi.call/return_dbl1.c libffi.call/return_dbl2.c libffi.call/return_fl.c \ libffi.call/return_fl1.c libffi.call/return_fl2.c libffi.call/return_fl3.c \ @@ -365,7 +369,7 @@ EXTRA_DIST = config/default.exp emscripten/build.sh emscripten/conftest.py \ libffi.complex/cls_complex_va_double.c libffi.complex/cls_complex_va_float.c libffi.complex/cls_complex_va_longdouble.c \ libffi.complex/complex.exp libffi.complex/complex.inc libffi.complex/complex_defs_double.inc \ libffi.complex/complex_defs_float.inc libffi.complex/complex_defs_longdouble.inc libffi.complex/complex_double.c \ - libffi.complex/complex_float.c libffi.complex/complex_int.c libffi.complex/complex_longdouble.c \ + libffi.complex/complex_float.c libffi.complex/complex_i128.c libffi.complex/complex_int.c libffi.complex/complex_longdouble.c \ libffi.complex/ffitest.h libffi.complex/many_complex.inc libffi.complex/many_complex_double.c \ libffi.complex/many_complex_float.c libffi.complex/many_complex_longdouble.c libffi.complex/return_complex.inc \ libffi.complex/return_complex1.inc libffi.complex/return_complex1_double.c libffi.complex/return_complex1_float.c \ diff --git a/deps/libffi/testsuite/lib/libffi.exp b/deps/libffi/testsuite/lib/libffi.exp index 0a98a07daf6702..55afb8f094a026 100644 --- a/deps/libffi/testsuite/lib/libffi.exp +++ b/deps/libffi/testsuite/lib/libffi.exp @@ -411,6 +411,10 @@ proc libffi_target_compile { source dest type options } { lappend options "libs= -lpthread" } + if { [string match "*-*-netbsd*" $target_triplet] } { + lappend options "libs= -lpthread" + } + lappend options "libs= -lffi" if { [string match "aarch64*-*-linux*" $target_triplet] } { diff --git a/deps/libffi/testsuite/libffi.call/i128-1.c b/deps/libffi/testsuite/libffi.call/i128-1.c new file mode 100644 index 00000000000000..1395f5e9426026 --- /dev/null +++ b/deps/libffi/testsuite/libffi.call/i128-1.c @@ -0,0 +1,103 @@ +/* Area: ffi_call + Purpose: Check int128 call and return. + Limitations: none. + PR: none. */ + +/* { dg-do run } */ +#include "ffitest.h" + +#if defined(FFI_TARGET_HAS_INT128) && defined(__SIZEOF_INT128__) + +typedef __int128_t i128; + +static const i128 val = ((i128)0x01020304050607ull << 64) | 0x08090a0b0c0d0e0full; +static const int dummy = 0xdeadbeef; + +#define D(X) int X __attribute__((unused)) + +static i128 f0(i128 x) +{ + return x; +} + +static i128 f1(D(a), i128 x) +{ + return x; +} + +static i128 f2(D(a), D(b), i128 x) +{ + return x; +} + +static i128 f3(D(a), D(b), D(c), i128 x) +{ + return x; +} + +static i128 f4(D(a), D(b), D(c), D(d), i128 x) +{ + return x; +} + +static i128 f5(D(a), D(b), D(c), D(d), D(e), i128 x) +{ + return x; +} + +static i128 f6(D(a), D(b), D(c), D(d), D(e), D(f), i128 x) +{ + return x; +} + +static i128 f7(D(a), D(b), D(c), D(d), D(e), D(f), D(g), i128 x) +{ + return x; +} + +static i128 f8(D(a), D(b), D(c), D(d), D(e), D(f), D(g), D(h), i128 x) +{ + return x; +} + +#define N 9 + +static void * const funcs[N] = { + f0, f1, f2, f3, f4, f5, f6, f7, f8 +}; + +int main (void) +{ + int i; + + for (i = 0; i < N; i++) + { + ffi_cif cif; + ffi_status s; + ffi_type *args[N]; + void *values[N]; + i128 ret; + int j; + + for (j = 0; j < i; j++) + { + args[j] = &ffi_type_sint; + values[j] = (void *)&dummy; + } + args[i] = &ffi_type_sint128; + values[i] = (void *)&val; + + s = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, i + 1, + &ffi_type_sint128, args); + CHECK(s == FFI_OK); + + ffi_call(&cif, FFI_FN(funcs[i]), &ret, values); + CHECK(ret == val); + } + + return 0; +} + +#else +int main (void) { return 0; } +#endif diff --git a/deps/libffi/testsuite/libffi.call/large_struct_by_value.c b/deps/libffi/testsuite/libffi.call/large_struct_by_value.c new file mode 100644 index 00000000000000..016870dfe1096c --- /dev/null +++ b/deps/libffi/testsuite/libffi.call/large_struct_by_value.c @@ -0,0 +1,63 @@ +/* Area: ffi_call + Purpose: Pass a large struct by value (more words than arg registers). + Limitations: none. + PR: none. + Originator: secscan regression (ARCompact used_stack overflow). + + Regression test: on ARCompact (ARC 32-bit), ffi_call_int sized the stack + argument area at two words per argument, but a by-value struct is marshalled + one word ("atom") at a time, and words beyond the argument registers were + written to that under-sized area without bound. Passing a 64-byte struct by + value (sixteen 32-bit words, eight more than the eight argument registers) + must marshal correctly and return the expected sum. */ + +/* { dg-do run } */ +#include "ffitest.h" + +typedef struct { int v[8]; } big_struct; + +static int ABI_ATTR +sum_big (big_struct s) +{ + int i, sum = 0; + for (i = 0; i < 8; i++) + sum += s.v[i]; + return sum; +} + +int main (void) +{ + ffi_cif cif; + ffi_type *args[1]; + void *values[1]; + ffi_type bs_type; + ffi_type *bs_elements[9]; + big_struct in; + ffi_arg result = 0; + int i, expected = 0; + + bs_type.size = 0; + bs_type.alignment = 0; + bs_type.type = FFI_TYPE_STRUCT; + for (i = 0; i < 8; i++) + bs_elements[i] = &ffi_type_sint; + bs_elements[8] = NULL; + bs_type.elements = bs_elements; + + for (i = 0; i < 8; i++) + { + in.v[i] = 0x1111 * (i + 1); + expected += in.v[i]; + } + + args[0] = &bs_type; + values[0] = ∈ + + CHECK(ffi_prep_cif(&cif, ABI_NUM, 1, &ffi_type_sint, args) == FFI_OK); + + ffi_call(&cif, FFI_FN(sum_big), &result, values); + + CHECK((int) result == expected); + + exit(0); +} diff --git a/deps/libffi/testsuite/libffi.call/many_small_structs.c b/deps/libffi/testsuite/libffi.call/many_small_structs.c new file mode 100644 index 00000000000000..242c38cfc135bf --- /dev/null +++ b/deps/libffi/testsuite/libffi.call/many_small_structs.c @@ -0,0 +1,92 @@ +/* Area: ffi_call + Purpose: Pass many small (8-byte) structs by value. + Limitations: none. + PR: none. + Originator: secscan regression (PA-RISC 32-bit slot under-count). + + Regression test: on PA-RISC 32-bit (FFI_PA32), ffi_size_stack_pa32 reserves + one stack slot per struct (pa/ffi.c: "z += 1"), but ffi_prep_args_pa32 + consumes two slots for a 5-8 byte struct passed inline. ffi_call_pa32 sets + the argument base to sp + cif->bytes and ffi_prep_args_pa32 writes each + argument at (base - slot*4), so once the marshaller's slot count exceeds + cif->bytes/4 the writes spill below sp -- first into the 64-byte register + save area, then over ffi_call_pa32's own saved return pointer. + + A few small structs only overflow into harmless scratch (the call still + returns correctly), so this uses enough 8-byte structs that the overflow + reaches the saved return pointer and corrupts the return path. On a correct + backend all arguments marshal within the allocated frame and the call simply + returns the expected sums; the test passes everywhere except an unfixed + FFI_PA32. */ + +/* { dg-do run } */ +#include "ffitest.h" + +typedef struct { int a; int b; } small_struct; + +#define NARGS 40 + +static small_struct ABI_ATTR +many_small (small_struct s0, small_struct s1, small_struct s2, small_struct s3, + small_struct s4, small_struct s5, small_struct s6, small_struct s7, + small_struct s8, small_struct s9, small_struct s10, small_struct s11, + small_struct s12, small_struct s13, small_struct s14, small_struct s15, + small_struct s16, small_struct s17, small_struct s18, small_struct s19, + small_struct s20, small_struct s21, small_struct s22, small_struct s23, + small_struct s24, small_struct s25, small_struct s26, small_struct s27, + small_struct s28, small_struct s29, small_struct s30, small_struct s31, + small_struct s32, small_struct s33, small_struct s34, small_struct s35, + small_struct s36, small_struct s37, small_struct s38, small_struct s39) +{ + small_struct r; + r.a = s0.a + s1.a + s2.a + s3.a + s4.a + s5.a + s6.a + s7.a + + s8.a + s9.a + s10.a + s11.a + s12.a + s13.a + s14.a + s15.a + + s16.a + s17.a + s18.a + s19.a + s20.a + s21.a + s22.a + s23.a + + s24.a + s25.a + s26.a + s27.a + s28.a + s29.a + s30.a + s31.a + + s32.a + s33.a + s34.a + s35.a + s36.a + s37.a + s38.a + s39.a; + r.b = s0.b + s1.b + s2.b + s3.b + s4.b + s5.b + s6.b + s7.b + + s8.b + s9.b + s10.b + s11.b + s12.b + s13.b + s14.b + s15.b + + s16.b + s17.b + s18.b + s19.b + s20.b + s21.b + s22.b + s23.b + + s24.b + s25.b + s26.b + s27.b + s28.b + s29.b + s30.b + s31.b + + s32.b + s33.b + s34.b + s35.b + s36.b + s37.b + s38.b + s39.b; + return r; +} + +int main (void) +{ + ffi_cif cif; + ffi_type *args[NARGS]; + void *values[NARGS]; + ffi_type ss_type; + ffi_type *ss_elements[3]; + small_struct in[NARGS]; + small_struct result = { 0, 0 }; + int i, expected_a = 0, expected_b = 0; + + ss_type.size = 0; + ss_type.alignment = 0; + ss_type.type = FFI_TYPE_STRUCT; + ss_type.elements = ss_elements; + ss_elements[0] = &ffi_type_sint; + ss_elements[1] = &ffi_type_sint; + ss_elements[2] = NULL; + + for (i = 0; i < NARGS; i++) + { + in[i].a = i + 1; + in[i].b = -(i + 1); + expected_a += in[i].a; + expected_b += in[i].b; + args[i] = &ss_type; + values[i] = &in[i]; + } + + CHECK(ffi_prep_cif(&cif, ABI_NUM, NARGS, &ss_type, args) == FFI_OK); + + ffi_call(&cif, FFI_FN(many_small), &result, values); + + CHECK(result.a == expected_a); + CHECK(result.b == expected_b); + + exit(0); +} diff --git a/deps/libffi/testsuite/libffi.call/plan.c b/deps/libffi/testsuite/libffi.call/plan.c new file mode 100644 index 00000000000000..afed0977e6b1cb --- /dev/null +++ b/deps/libffi/testsuite/libffi.call/plan.c @@ -0,0 +1,127 @@ +/* Area: ffi_call_plan + Purpose: Check that a reusable call plan reproduces ffi_call for the + pure-GP64 fast path, pointer arguments and repeated reuse, + and that a signature with no fast path still yields a usable + plan that falls back to ffi_call. + Limitations: none. + PR: none. + Originator: ffi_call_plan tests */ + +/* { dg-do run } */ +#include "ffitest.h" + +static uint64_t gp6(uint64_t a, uint64_t b, uint64_t c, + uint64_t d, uint64_t e, uint64_t f) +{ + return a + b * 2 + c * 3 + d * 4 + e * 5 + f * 6; +} + +static void *ptr_ident(void *p) +{ + return p; +} + +struct small_pair { long x; long y; }; + +static long ssum(struct small_pair s) +{ + return s.x - s.y; +} + +int main (void) +{ + ffi_cif cif; + ffi_type *args[6]; + void *values[6]; + ffi_call_plan *plan; + uint64_t a[6], r_call, r_plan; + int i, k; + + /* Pure GP64: every argument is one 64-bit integer, so build_plan + selects the ffi_plan_gpN direct thunk. Reuse the plan across many + invocations with changing values. */ + for (i = 0; i < 6; i++) + args[i] = &ffi_type_uint64; + CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 6, &ffi_type_uint64, args) + == FFI_OK); + plan = ffi_call_plan_alloc(&cif); + CHECK(plan != NULL); + + for (k = 0; k < 100; k++) + { + for (i = 0; i < 6; i++) + { + a[i] = (uint64_t) (k * 7 + i + 1); + values[i] = &a[i]; + } + ffi_call(&cif, FFI_FN(gp6), &r_call, values); + ffi_call_plan_invoke(plan, FFI_FN(gp6), &r_plan, values); + CHECK(r_call == r_plan); + CHECK(r_plan == gp6(a[0], a[1], a[2], a[3], a[4], a[5])); + } + ffi_call_plan_free(plan); + + /* Pointer argument and pointer return. */ + { + ffi_cif cifp; + ffi_type *pargs[1]; + void *pvalues[1]; + void *in, *rc, *rp; + ffi_call_plan *planp; + + pargs[0] = &ffi_type_pointer; + CHECK(ffi_prep_cif(&cifp, FFI_DEFAULT_ABI, 1, &ffi_type_pointer, pargs) + == FFI_OK); + planp = ffi_call_plan_alloc(&cifp); + CHECK(planp != NULL); + + in = &cifp; + pvalues[0] = ∈ + ffi_call(&cifp, FFI_FN(ptr_ident), &rc, pvalues); + ffi_call_plan_invoke(planp, FFI_FN(ptr_ident), &rp, pvalues); + CHECK(rc == in); + CHECK(rp == in); + ffi_call_plan_free(planp); + } + + /* No fast path: a struct-by-value argument. build_plan returns NULL for + the fast plan, but ffi_call_plan_alloc must still hand back a valid plan + whose invoke falls back to ffi_call and produces the same result. */ + { + ffi_cif cifs; + ffi_type *sargs[1]; + ffi_type stype; + ffi_type *selements[3]; + void *svalues[1]; + struct small_pair s; + ffi_arg rc, rp; + ffi_call_plan *plans; + + selements[0] = &ffi_type_slong; + selements[1] = &ffi_type_slong; + selements[2] = NULL; + stype.size = stype.alignment = 0; + stype.type = FFI_TYPE_STRUCT; + stype.elements = selements; + + sargs[0] = &stype; + CHECK(ffi_prep_cif(&cifs, FFI_DEFAULT_ABI, 1, &ffi_type_slong, sargs) + == FFI_OK); + plans = ffi_call_plan_alloc(&cifs); + CHECK(plans != NULL); + + s.x = 123; + s.y = 45; + svalues[0] = &s; + ffi_call(&cifs, FFI_FN(ssum), &rc, svalues); + ffi_call_plan_invoke(plans, FFI_FN(ssum), &rp, svalues); + CHECK(rc == rp); + CHECK((long) rp == ssum(s)); + ffi_call_plan_free(plans); + } + + /* Freeing NULL is documented to be harmless. */ + ffi_call_plan_free(NULL); + + exit(0); +} diff --git a/deps/libffi/testsuite/libffi.call/plan_mixed.c b/deps/libffi/testsuite/libffi.call/plan_mixed.c new file mode 100644 index 00000000000000..54b1f6290c4422 --- /dev/null +++ b/deps/libffi/testsuite/libffi.call/plan_mixed.c @@ -0,0 +1,153 @@ +/* Area: ffi_call_plan + Purpose: Check that a reusable call plan reproduces ffi_call for + signatures that mix general-purpose and SSE registers, for + float and double returns, for signed narrow arguments, and + for integer returns narrower than a register (which the plan + must widen to ffi_arg exactly as ffi_call does). + Limitations: none. + PR: none. + Originator: ffi_call_plan tests */ + +/* { dg-do run } */ +#include "ffitest.h" + +static double mixed(int a, double b, long c, float d, + long long e, double f) +{ + return (double) a + b + (double) c + (double) d + (double) e + f; +} + +static float faddf(float a, float b) +{ + return a + b; +} + +static signed char ret_sc(signed char x) +{ + return (signed char) (x + 1); +} + +static unsigned char ret_uc(unsigned char x) +{ + return (unsigned char) (x + 1); +} + +int main (void) +{ + /* Mixed GP + SSE arguments, double return: exercises the fast local-image + path with both integer and SSE moves and the ssecount (al) setup. */ + { + ffi_cif cif; + ffi_type *args[6]; + void *values[6]; + ffi_call_plan *plan; + int a = 3; + double b = 1.5, f = -2.25, rc, rp; + long c = 7; + float d = 0.5f; + long long e = -11; + + args[0] = &ffi_type_sint; + args[1] = &ffi_type_double; + args[2] = &ffi_type_slong; + args[3] = &ffi_type_float; + args[4] = &ffi_type_sint64; + args[5] = &ffi_type_double; + values[0] = &a; values[1] = &b; values[2] = &c; + values[3] = &d; values[4] = &e; values[5] = &f; + + CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 6, &ffi_type_double, args) + == FFI_OK); + plan = ffi_call_plan_alloc(&cif); + CHECK(plan != NULL); + + ffi_call(&cif, FFI_FN(mixed), &rc, values); + ffi_call_plan_invoke(plan, FFI_FN(mixed), &rp, values); + CHECK_DOUBLE_EQ(rc, rp); + CHECK_DOUBLE_EQ(rp, mixed(a, b, c, d, e, f)); + ffi_call_plan_free(plan); + } + + /* Float arguments and float return. */ + { + ffi_cif cif; + ffi_type *args[2]; + void *values[2]; + ffi_call_plan *plan; + float a = 1.25f, b = 2.5f, rc, rp; + + args[0] = &ffi_type_float; + args[1] = &ffi_type_float; + values[0] = &a; + values[1] = &b; + CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 2, &ffi_type_float, args) + == FFI_OK); + plan = ffi_call_plan_alloc(&cif); + CHECK(plan != NULL); + + ffi_call(&cif, FFI_FN(faddf), &rc, values); + ffi_call_plan_invoke(plan, FFI_FN(faddf), &rp, values); + CHECK_FLOAT_EQ(rc, rp); + CHECK_FLOAT_EQ(rp, faddf(a, b)); + ffi_call_plan_free(plan); + } + + /* Signed narrow argument and signed narrow return: the plan sign-extends + the argument and widens the return to ffi_arg just as ffi_call does. */ + { + ffi_cif cif; + ffi_type *args[1]; + void *values[1]; + ffi_call_plan *plan; + signed char in; + ffi_arg rc, rp; + int v; + + args[0] = &ffi_type_schar; + CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 1, &ffi_type_schar, args) + == FFI_OK); + plan = ffi_call_plan_alloc(&cif); + CHECK(plan != NULL); + + for (v = -128; v < 128; v++) + { + in = (signed char) v; + values[0] = ∈ + ffi_call(&cif, FFI_FN(ret_sc), &rc, values); + ffi_call_plan_invoke(plan, FFI_FN(ret_sc), &rp, values); + CHECK(rc == rp); + CHECK((signed char) rp == ret_sc(in)); + } + ffi_call_plan_free(plan); + } + + /* Unsigned narrow argument and unsigned narrow return. */ + { + ffi_cif cif; + ffi_type *args[1]; + void *values[1]; + ffi_call_plan *plan; + unsigned char in; + ffi_arg rc, rp; + int v; + + args[0] = &ffi_type_uchar; + CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 1, &ffi_type_uchar, args) + == FFI_OK); + plan = ffi_call_plan_alloc(&cif); + CHECK(plan != NULL); + + for (v = 0; v < 256; v++) + { + in = (unsigned char) v; + values[0] = ∈ + ffi_call(&cif, FFI_FN(ret_uc), &rc, values); + ffi_call_plan_invoke(plan, FFI_FN(ret_uc), &rp, values); + CHECK(rc == rp); + CHECK((unsigned char) rp == ret_uc(in)); + } + ffi_call_plan_free(plan); + } + + exit(0); +} diff --git a/deps/libffi/testsuite/libffi.call/plan_spill.c b/deps/libffi/testsuite/libffi.call/plan_spill.c new file mode 100644 index 00000000000000..c073c99142e423 --- /dev/null +++ b/deps/libffi/testsuite/libffi.call/plan_spill.c @@ -0,0 +1,142 @@ +/* Area: ffi_call_plan + Purpose: Check that a reusable call plan reproduces ffi_call when + arguments spill past the argument registers onto the stack. + This drives the non-fast plan path (a move list handed to + ffi_call_unix64) for GP spill, SSE spill, and a mix of both. + Limitations: none. + PR: none. + Originator: ffi_call_plan tests */ + +/* { dg-do run } */ +#include "ffitest.h" + +/* 10 integers: 6 in registers, 4 spilled to the stack. */ +static long long gp10(long long a1, long long a2, long long a3, long long a4, + long long a5, long long a6, long long a7, long long a8, + long long a9, long long a10) +{ + return a1 + a2 * 2 + a3 * 3 + a4 * 4 + a5 * 5 + + a6 * 6 + a7 * 7 + a8 * 8 + a9 * 9 + a10 * 10; +} + +/* 12 doubles: 8 in SSE registers, 4 spilled to the stack. */ +static double sse12(double a1, double a2, double a3, double a4, + double a5, double a6, double a7, double a8, + double a9, double a10, double a11, double a12) +{ + return a1 + a2 * 2 + a3 * 3 + a4 * 4 + a5 * 5 + a6 * 6 + + a7 * 7 + a8 * 8 + a9 * 9 + a10 * 10 + a11 * 11 + a12 * 12; +} + +/* 8 ints + 8 doubles: both classes spill. */ +static double mix16(long i1, long i2, long i3, long i4, + long i5, long i6, long i7, long i8, + double d1, double d2, double d3, double d4, + double d5, double d6, double d7, double d8) +{ + double s = 0.0; + s += (double) (i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8); + s += d1 + d2 + d3 + d4 + d5 + d6 + d7 + d8; + return s; +} + +int main (void) +{ + /* GP spill. */ + { + ffi_cif cif; + ffi_type *args[10]; + void *values[10]; + long long a[10]; + long long rc, rp; + ffi_call_plan *plan; + int i; + + for (i = 0; i < 10; i++) + { + args[i] = &ffi_type_sint64; + a[i] = (long long) (i + 1) * 100 - 3; + values[i] = &a[i]; + } + CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 10, &ffi_type_sint64, args) + == FFI_OK); + plan = ffi_call_plan_alloc(&cif); + CHECK(plan != NULL); + + ffi_call(&cif, FFI_FN(gp10), &rc, values); + ffi_call_plan_invoke(plan, FFI_FN(gp10), &rp, values); + CHECK(rc == rp); + CHECK(rp == gp10(a[0], a[1], a[2], a[3], a[4], + a[5], a[6], a[7], a[8], a[9])); + ffi_call_plan_free(plan); + } + + /* SSE spill. */ + { + ffi_cif cif; + ffi_type *args[12]; + void *values[12]; + double a[12]; + double rc, rp; + ffi_call_plan *plan; + int i; + + for (i = 0; i < 12; i++) + { + args[i] = &ffi_type_double; + a[i] = (double) i + 0.25; + values[i] = &a[i]; + } + CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 12, &ffi_type_double, args) + == FFI_OK); + plan = ffi_call_plan_alloc(&cif); + CHECK(plan != NULL); + + ffi_call(&cif, FFI_FN(sse12), &rc, values); + ffi_call_plan_invoke(plan, FFI_FN(sse12), &rp, values); + CHECK_DOUBLE_EQ(rc, rp); + CHECK_DOUBLE_EQ(rp, sse12(a[0], a[1], a[2], a[3], a[4], a[5], + a[6], a[7], a[8], a[9], a[10], a[11])); + ffi_call_plan_free(plan); + } + + /* Both classes spill. */ + { + ffi_cif cif; + ffi_type *args[16]; + void *values[16]; + long iv[8]; + double dv[8]; + double rc, rp; + ffi_call_plan *plan; + int i; + + for (i = 0; i < 8; i++) + { + args[i] = &ffi_type_slong; + iv[i] = (long) (i + 1); + values[i] = &iv[i]; + } + for (i = 0; i < 8; i++) + { + args[8 + i] = &ffi_type_double; + dv[i] = (double) (i + 1) * 0.5; + values[8 + i] = &dv[i]; + } + CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 16, &ffi_type_double, args) + == FFI_OK); + plan = ffi_call_plan_alloc(&cif); + CHECK(plan != NULL); + + ffi_call(&cif, FFI_FN(mix16), &rc, values); + ffi_call_plan_invoke(plan, FFI_FN(mix16), &rp, values); + CHECK_DOUBLE_EQ(rc, rp); + CHECK_DOUBLE_EQ(rp, mix16(iv[0], iv[1], iv[2], iv[3], + iv[4], iv[5], iv[6], iv[7], + dv[0], dv[1], dv[2], dv[3], + dv[4], dv[5], dv[6], dv[7])); + ffi_call_plan_free(plan); + } + + exit(0); +} diff --git a/deps/libffi/testsuite/libffi.call/plan_struct.c b/deps/libffi/testsuite/libffi.call/plan_struct.c new file mode 100644 index 00000000000000..816296d77a8afa --- /dev/null +++ b/deps/libffi/testsuite/libffi.call/plan_struct.c @@ -0,0 +1,163 @@ +/* Area: ffi_call_plan + Purpose: Check that a reusable call plan reproduces ffi_call for struct + returns. A struct return does not disable planning (only a + struct *argument* does), so this drives both the in-memory + return path (RET_IN_MEM, including a NULL rvalue) and the + register-pair struct return path, plus a large struct argument + that forces the ffi_call by-value copy fallback. + Limitations: none. + PR: none. + Originator: ffi_call_plan tests */ + +/* { dg-do run } */ +#include "ffitest.h" + +static int call_count = 0; + +/* 24 bytes: returned in memory (a hidden pointer in the first argument). */ +struct big3 { double a, b, c; }; + +static struct big3 make_big3(double a, double b, double c) +{ + struct big3 r; + call_count++; + r.a = a + 1.0; + r.b = b + 2.0; + r.c = c + 3.0; + return r; +} + +/* A struct larger than 16 bytes passed by value forces ffi_call to make a + copy; build_plan has no fast path for a struct argument, so this exercises + the plan's fallback to ffi_call. */ +static double sum_big3(struct big3 s) +{ + return s.a + s.b + s.c; +} + +/* 16 bytes: returned in a register pair (RAX:RDX on x86-64). */ +struct pair2 { long x, y; }; + +static struct pair2 make_pair2(long x, long y) +{ + struct pair2 r; + r.x = x * 2; + r.y = y * 3; + return r; +} + +int main (void) +{ + ffi_type *big3_elements[4]; + ffi_type big3_t; + ffi_type *pair2_elements[3]; + ffi_type pair2_t; + + big3_elements[0] = &ffi_type_double; + big3_elements[1] = &ffi_type_double; + big3_elements[2] = &ffi_type_double; + big3_elements[3] = NULL; + big3_t.size = big3_t.alignment = 0; + big3_t.type = FFI_TYPE_STRUCT; + big3_t.elements = big3_elements; + + pair2_elements[0] = &ffi_type_slong; + pair2_elements[1] = &ffi_type_slong; + pair2_elements[2] = NULL; + pair2_t.size = pair2_t.alignment = 0; + pair2_t.type = FFI_TYPE_STRUCT; + pair2_t.elements = pair2_elements; + + /* In-memory struct return with scalar arguments. */ + { + ffi_cif cif; + ffi_type *args[3]; + void *values[3]; + ffi_call_plan *plan; + double a = 10.0, b = 20.0, c = 30.0; + struct big3 rc, rp; + int before; + + args[0] = &ffi_type_double; + args[1] = &ffi_type_double; + args[2] = &ffi_type_double; + values[0] = &a; + values[1] = &b; + values[2] = &c; + CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 3, &big3_t, args) == FFI_OK); + plan = ffi_call_plan_alloc(&cif); + CHECK(plan != NULL); + + ffi_call(&cif, FFI_FN(make_big3), &rc, values); + ffi_call_plan_invoke(plan, FFI_FN(make_big3), &rp, values); + CHECK_DOUBLE_EQ(rc.a, rp.a); + CHECK_DOUBLE_EQ(rc.b, rp.b); + CHECK_DOUBLE_EQ(rc.c, rp.c); + CHECK_DOUBLE_EQ(rp.a, a + 1.0); + CHECK_DOUBLE_EQ(rp.b, b + 2.0); + CHECK_DOUBLE_EQ(rp.c, c + 3.0); + + /* A NULL rvalue for an in-memory struct return must not crash: libffi + supplies scratch space and discards the result, but the callee still + runs. Confirm the call actually happened. */ + before = call_count; + ffi_call_plan_invoke(plan, FFI_FN(make_big3), NULL, values); + CHECK(call_count == before + 1); + + ffi_call_plan_free(plan); + } + + /* Large struct argument: no fast path, falls back to ffi_call. */ + { + ffi_cif cif; + ffi_type *args[1]; + void *values[1]; + ffi_call_plan *plan; + struct big3 s; + double rc, rp; + + s.a = 1.5; + s.b = 2.5; + s.c = 3.5; + args[0] = &big3_t; + values[0] = &s; + CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 1, &ffi_type_double, args) + == FFI_OK); + plan = ffi_call_plan_alloc(&cif); + CHECK(plan != NULL); + + ffi_call(&cif, FFI_FN(sum_big3), &rc, values); + ffi_call_plan_invoke(plan, FFI_FN(sum_big3), &rp, values); + CHECK_DOUBLE_EQ(rc, rp); + CHECK_DOUBLE_EQ(rp, sum_big3(s)); + ffi_call_plan_free(plan); + } + + /* Register-pair struct return. */ + { + ffi_cif cif; + ffi_type *args[2]; + void *values[2]; + ffi_call_plan *plan; + long x = 7, y = 11; + struct pair2 rc, rp; + + args[0] = &ffi_type_slong; + args[1] = &ffi_type_slong; + values[0] = &x; + values[1] = &y; + CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 2, &pair2_t, args) == FFI_OK); + plan = ffi_call_plan_alloc(&cif); + CHECK(plan != NULL); + + ffi_call(&cif, FFI_FN(make_pair2), &rc, values); + ffi_call_plan_invoke(plan, FFI_FN(make_pair2), &rp, values); + CHECK(rc.x == rp.x); + CHECK(rc.y == rp.y); + CHECK(rp.x == x * 2); + CHECK(rp.y == y * 3); + ffi_call_plan_free(plan); + } + + exit(0); +} diff --git a/deps/libffi/testsuite/libffi.call/plan_var.c b/deps/libffi/testsuite/libffi.call/plan_var.c new file mode 100644 index 00000000000000..e630156b23f077 --- /dev/null +++ b/deps/libffi/testsuite/libffi.call/plan_var.c @@ -0,0 +1,73 @@ +/* Area: ffi_call_plan + Purpose: Check that a reusable call plan reproduces ffi_call for a + variadic signature prepared with ffi_prep_cif_var. The + variadic double arguments travel in SSE registers, so the + fast path must set the vector-register count (al) correctly. + Limitations: none. + PR: none. + Originator: ffi_call_plan tests */ + +/* { dg-do run } */ +#include +#include "ffitest.h" + +static double vsum(int n, ...) +{ + va_list ap; + double s = 0.0; + int i; + + va_start(ap, n); + for (i = 0; i < n; i++) + s += va_arg(ap, double); + va_end(ap); + return s; +} + +static void +run (int nvar) +{ + ffi_cif cif; + ffi_type *args[1 + 8]; + void *values[1 + 8]; + double d[8]; + int n = nvar; + double rc, rp, expect; + ffi_call_plan *plan; + int i; + + CHECK(nvar <= 8); + + args[0] = &ffi_type_sint; + values[0] = &n; + expect = 0.0; + for (i = 0; i < nvar; i++) + { + d[i] = (double) (i + 1) * 1.5; + args[1 + i] = &ffi_type_double; + values[1 + i] = &d[i]; + expect += d[i]; + } + + CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 1, 1 + nvar, + &ffi_type_double, args) == FFI_OK); + plan = ffi_call_plan_alloc(&cif); + CHECK(plan != NULL); + + ffi_call(&cif, FFI_FN(vsum), &rc, values); + ffi_call_plan_invoke(plan, FFI_FN(vsum), &rp, values); + CHECK_DOUBLE_EQ(rc, rp); + CHECK_DOUBLE_EQ(rp, expect); + + ffi_call_plan_free(plan); +} + +int main (void) +{ + run (0); + run (1); + run (3); + run (5); + run (8); + exit(0); +} diff --git a/deps/libffi/testsuite/libffi.closures/closure.exp b/deps/libffi/testsuite/libffi.closures/closure.exp index ed4145ca843b4a..9d53294909ee73 100644 --- a/deps/libffi/testsuite/libffi.closures/closure.exp +++ b/deps/libffi/testsuite/libffi.closures/closure.exp @@ -36,7 +36,7 @@ if { [string match $compiler_vendor "microsoft"] } { set tlist [lsort [glob -nocomplain -- $srcdir/$subdir/*.c]] if { [libffi_feature_test "#if FFI_CLOSURES"] } { - run-many-tests $tlist "" + run-many-tests $tlist $additional_options } else { foreach test $tlist { unsupported "$test" diff --git a/deps/libffi/testsuite/libffi.closures/closure_loc_fn0.c b/deps/libffi/testsuite/libffi.closures/closure_loc_fn0.c index f344a6074e1429..179485d9b57b87 100644 --- a/deps/libffi/testsuite/libffi.closures/closure_loc_fn0.c +++ b/deps/libffi/testsuite/libffi.closures/closure_loc_fn0.c @@ -6,6 +6,7 @@ PR: none. Originator: 20030828 */ +/* { dg-do run } */ #include "ffitest.h" @@ -80,8 +81,10 @@ int main (void) CHECK(ffi_prep_closure_loc(pcl, &cif, closure_loc_test_fn0, (void *) 3 /* userdata */, codeloc) == FFI_OK); -#if !defined(FFI_EXEC_STATIC_TRAMP) && !defined(__EMSCRIPTEN__) - /* With static trampolines, the codeloc does not point to closure */ +#if !defined(FFI_EXEC_STATIC_TRAMP) && !defined(__EMSCRIPTEN__) \ + && !(defined(FFI_EXEC_TRAMPOLINE_TABLE) && FFI_EXEC_TRAMPOLINE_TABLE) + /* With static trampolines or a trampoline table (Apple aarch64), the + codeloc does not point to the closure */ CHECK(memcmp(pcl, FFI_CL(codeloc), sizeof(*pcl)) == 0); #endif diff --git a/deps/libffi/testsuite/libffi.complex/complex_i128.c b/deps/libffi/testsuite/libffi.complex/complex_i128.c new file mode 100644 index 00000000000000..a599d556e12083 --- /dev/null +++ b/deps/libffi/testsuite/libffi.complex/complex_i128.c @@ -0,0 +1,118 @@ +/* Area: ffi_call + Purpose: Check complex int128 call and return. + Limitations: none. + PR: none. */ + +/* { dg-do run } */ +#include "ffitest.h" + +/* clang defines __SIZEOF_INT128__ but does not support _Complex __int128, + so exclude it here and fall through to the trivial main() below. */ +#if defined(FFI_TARGET_HAS_INT128) && \ + defined(FFI_TARGET_HAS_COMPLEX_TYPE) && \ + defined(__SIZEOF_INT128__) && \ + !defined(__clang__) + +typedef __int128_t i128; +typedef _Complex __int128 ci128; + +static const ci128 val = + (((i128)0x01020304050607ull << 64) | 0x08090a0b0c0d0e0full) + + (((i128)0x10203040506070ull << 64) | 0x8090a0b0c0d0e0f0ull) * 1i; +static const int dummy = 0xdeadbeef; + +#define D(X) int X __attribute__((unused)) + +static ci128 f0(ci128 x) +{ + return x; +} + +static ci128 f1(D(a), ci128 x) +{ + return x; +} + +static ci128 f2(D(a), D(b), ci128 x) +{ + return x; +} + +static ci128 f3(D(a), D(b), D(c), ci128 x) +{ + return x; +} + +static ci128 f4(D(a), D(b), D(c), D(d), ci128 x) +{ + return x; +} + +static ci128 f5(D(a), D(b), D(c), D(d), D(e), ci128 x) +{ + return x; +} + +static ci128 f6(D(a), D(b), D(c), D(d), D(e), D(f), ci128 x) +{ + return x; +} + +static ci128 f7(D(a), D(b), D(c), D(d), D(e), D(f), D(g), ci128 x) +{ + return x; +} + +static ci128 f8(D(a), D(b), D(c), D(d), D(e), D(f), D(g), D(h), ci128 x) +{ + return x; +} + +#define N 9 + +static void * const funcs[N] = { + f0, f1, f2, f3, f4, f5, f6, f7, f8 +}; + +static ffi_type ffi_type_ci128 = { + sizeof(ci128), + _Alignof(ci128), + FFI_TYPE_COMPLEX, + (ffi_type *[2]){ &ffi_type_sint128, NULL }, +}; + +int main (void) +{ + int i; + + for (i = 0; i < N; i++) + { + ffi_cif cif; + ffi_status s; + ffi_type *args[N]; + void *values[N]; + ci128 ret; + int j; + + for (j = 0; j < i; j++) + { + args[j] = &ffi_type_sint; + values[j] = (void *)&dummy; + } + args[i] = &ffi_type_ci128; + values[i] = (void *)&val; + + s = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, i + 1, + &ffi_type_ci128, args); + CHECK(s == FFI_OK); + + ffi_call(&cif, FFI_FN(funcs[i]), &ret, values); + CHECK(ret == val); + } + + return 0; +} + +#else +int main (void) { return 0; } +#endif diff --git a/deps/libffi/testsuite/libffi.go/go.exp b/deps/libffi/testsuite/libffi.go/go.exp index 100c5e75b40915..f477e99ade6911 100644 --- a/deps/libffi/testsuite/libffi.go/go.exp +++ b/deps/libffi/testsuite/libffi.go/go.exp @@ -19,10 +19,23 @@ libffi-init global srcdir subdir +if { [string match $compiler_vendor "microsoft"] } { + # -wd4005 macro redefinition + # -wd4244 implicit conversion to type of smaller size + # -wd4305 truncation to smaller type + # -wd4477 printf %lu of uintptr_t + # -wd4312 implicit conversion to type of greater size + # -wd4311 pointer truncation to unsigned long + # -EHsc C++ Exception Handling (no SEH exceptions) + set additional_options "-wd4005 -wd4244 -wd4305 -wd4477 -wd4312 -wd4311 -EHsc"; +} else { + set additional_options ""; +} + set tlist [lsort [glob -nocomplain -- $srcdir/$subdir/*.{c,cc}]] if { [libffi_feature_test "#ifdef FFI_GO_CLOSURES"] } { - run-many-tests $tlist "" + run-many-tests $tlist $additional_options } else { foreach test $tlist { unsupported "$test"