From 1c854ffe8817dad698214915016e552e70682724 Mon Sep 17 00:00:00 2001 From: Paolo Insogna Date: Thu, 7 May 2026 15:18:03 +0200 Subject: [PATCH 1/5] deps: add missing static linking targets for libffi Signed-off-by: Paolo Insogna --- configure.py | 19 ++-- deps/libffi/generate-headers.py | 106 ++++++++++++------- deps/libffi/libffi.gyp | 174 ++++++++++++++++++++++++++++---- doc/api/ffi.md | 15 +-- 4 files changed, 248 insertions(+), 66 deletions(-) diff --git a/configure.py b/configure.py index 7b82e0c5e86e7d..50200d5b87437d 100755 --- a/configure.py +++ b/configure.py @@ -2310,17 +2310,20 @@ def without_sqlite_error(option): configure_library('sqlite', o, pkgname='sqlite3') def bundled_ffi_supported(os_name, target_arch): - supported = { - 'freebsd': {'arm', 'arm64', 'x64'}, - 'linux': {'arm', 'arm64', 'x64'}, - 'mac': {'arm64', 'x64'}, - 'win': {'arm64', 'x64'}, - } - if target_arch == 'x86': target_arch = 'ia32' - return target_arch in supported.get(os_name, set()) + if target_arch in {'arm', 'arm64', 'ia32', 'x64', 'x86_64', + 's390x', 'riscv64', 'loong64'}: + return True + + if target_arch in {'mips', 'mipsel', 'mips64el'}: + return os_name in {'freebsd', 'linux', 'openbsd'} + + if target_arch == 'ppc64': + return os_name in {'aix', 'freebsd', 'linux', 'mac', 'openbsd'} + + return False def configure_ffi(o): use_ffi = not options.without_ffi diff --git a/deps/libffi/generate-headers.py b/deps/libffi/generate-headers.py index 30a460bb27869c..ef91278f5b9fd3 100644 --- a/deps/libffi/generate-headers.py +++ b/deps/libffi/generate-headers.py @@ -10,33 +10,66 @@ LIBFFI_VERSION = '3.5.2' LIBFFI_VERSION_NUMBER = '30502' -TARGETS = { - ('freebsd', 'arm'): ('ARM', 'arm'), - ('freebsd', 'arm64'): ('AARCH64', 'aarch64'), - ('freebsd', 'x64'): ('X86_64', 'x86'), - ('linux', 'arm'): ('ARM', 'arm'), - ('linux', 'arm64'): ('AARCH64', 'aarch64'), - ('linux', 'x64'): ('X86_64', 'x86'), - ('mac', 'arm64'): ('AARCH64', 'aarch64'), - ('mac', 'x64'): ('X86_64', 'x86'), - ('win', 'arm'): ('ARM_WIN32', 'arm'), - ('win', 'arm64'): ('ARM_WIN64', 'aarch64'), - ('win', 'x64'): ('X86_WIN64', 'x86'), -} +def normalize_arch(target_arch): + aliases = { + 'x86': 'ia32', + 'x86_64': 'x64', + 'arm64': 'arm64', + 'aarch64': 'arm64', + } + return aliases.get(target_arch, target_arch) def get_target(os_name, target_arch): - try: - return TARGETS[(os_name, target_arch)] - except KeyError as exc: - supported = ', '.join( - f'{os_name}/{arch}' for os_name, arch in sorted(TARGETS)) - raise ValueError( - f'Unsupported libffi target {os_name}/{target_arch}. ' - f'Supported targets: {supported}.') from exc + target_arch = normalize_arch(target_arch) + + if target_arch == 'arm': + return ('ARM_WIN32' if os_name == 'win' else 'ARM', 'arm') + + if target_arch == 'arm64': + return ('ARM_WIN64' if os_name == 'win' else 'AARCH64', 'aarch64') + + if target_arch == 'ia32': + if os_name in ('freebsd', 'openbsd'): + return ('X86_FREEBSD', 'x86') + if os_name in ('ios', 'mac'): + return ('X86_DARWIN', 'x86') + if os_name == 'win': + return ('X86_WIN32', 'x86') + return ('X86', 'x86') + + if target_arch == 'x64': + return ('X86_WIN64' if os_name == 'win' else 'X86_64', 'x86') + + if target_arch == 's390x': + return ('S390', 's390') + + if target_arch == 'riscv64': + return ('RISCV', 'riscv') + + if target_arch == 'loong64': + return ('LOONGARCH64', 'loongarch64') + + if target_arch in ('mips', 'mipsel', 'mips64el'): + if os_name in ('freebsd', 'linux', 'openbsd'): + return ('MIPS', 'mips') + + if target_arch == 'ppc64': + if os_name == 'aix': + return ('POWERPC_AIX', 'powerpc') + if os_name == 'mac': + return ('POWERPC_DARWIN', 'powerpc') + if os_name in ('freebsd', 'openbsd'): + return ('POWERPC_FREEBSD', 'powerpc') + if os_name == 'linux': + return ('POWERPC', 'powerpc') + + raise ValueError(f'Unsupported libffi target {os_name}/{target_arch}.') def has_long_double(os_name, target_arch): + target_arch = normalize_arch(target_arch) + if os_name == 'win': return '0' @@ -50,6 +83,7 @@ def has_long_double(os_name, target_arch): def uses_exec_trampoline_table(os_name, target_arch): + target_arch = normalize_arch(target_arch) return os_name == 'mac' and target_arch in ('arm', 'arm64') @@ -72,6 +106,7 @@ def render_ffi_header(base_dir, os_name, target_arch, target): def render_fficonfig(os_name, target_arch): + target_arch = normalize_arch(target_arch) lines = [ '/* Auto-generated by generate-headers.py. */', '#ifndef LIBFFI_FFICONFIG_H_', @@ -94,15 +129,15 @@ def render_fficonfig(os_name, target_arch): '#define EH_FRAME_FLAGS "a"', ]) - if target_arch in ('x64', 'x86') and os_name != 'win': + if target_arch in ('ia32', 'x64') and os_name != 'win': lines.append('#define HAVE_AS_X86_PCREL 1') if uses_exec_trampoline_table(os_name, target_arch): lines.append('#define FFI_EXEC_TRAMPOLINE_TABLE 1') - elif os_name in ('freebsd', 'mac'): + elif os_name in ('freebsd', 'mac', 'openbsd'): lines.append('#define FFI_MMAP_EXEC_WRIT 1') - if os_name == 'linux': + if os_name != 'win': lines.extend([ '#define HAVE_DLFCN_H 1', '#define HAVE_MMAP 1', @@ -111,15 +146,10 @@ def render_fficonfig(os_name, target_arch): '#define HAVE_SYS_MMAN_H 1', '#define HAVE_UNISTD_H 1', ]) - elif os_name in ('freebsd', 'mac'): + + if os_name in ('freebsd', 'ios', 'mac', 'openbsd'): lines.extend([ - '#define HAVE_DLFCN_H 1', - '#define HAVE_MMAP 1', - '#define HAVE_MMAP_ANON 1', '#define HAVE_MMAP_DEV_ZERO 1', - '#define HAVE_MMAP_FILE 1', - '#define HAVE_SYS_MMAN_H 1', - '#define HAVE_UNISTD_H 1', ]) if os_name == 'mac' and target_arch == 'arm64': @@ -204,12 +234,20 @@ def detect_target_arch(): 'x86_64': 'x64', 'x64': 'x64', 'win32': 'x64', - 'i386': 'x86', - 'i686': 'x86', - 'x86': 'x86', + 'i386': 'ia32', + 'i686': 'ia32', + 'ia32': 'ia32', + 'x86': 'ia32', 'arm64': 'arm64', 'aarch64': 'arm64', 'arm': 'arm', + 's390x': 's390x', + 'riscv64': 'riscv64', + 'loong64': 'loong64', + 'ppc64': 'ppc64', + 'mips': 'mips', + 'mipsel': 'mipsel', + 'mips64el': 'mips64el', } for candidate in candidates: diff --git a/deps/libffi/libffi.gyp b/deps/libffi/libffi.gyp index cc83695f45b6ab..2d6cccd51a3097 100644 --- a/deps/libffi/libffi.gyp +++ b/deps/libffi/libffi.gyp @@ -15,7 +15,14 @@ 'conditions': [ ['OS == "win"', { 'conditions': [ - ['target_arch == "x64"', { + ['target_arch == "ia32" or target_arch == "x86"', { + 'variables': { + 'libffi_arch_sources': [ + 'src/x86/ffi.c', + ], + }, + }], + ['target_arch == "x64" or target_arch == "x86_64"', { 'variables': { 'libffi_arch_sources': [ 'src/x86/ffiw64.c', @@ -26,7 +33,6 @@ 'variables': { 'libffi_arch_sources': [ 'src/arm/ffi.c', - 'src/arm/sysv_msvc_arm32.S', ], }, }], @@ -39,9 +45,17 @@ }], ], }], - ['OS == "linux" or OS == "freebsd"', { + ['OS != "win"', { 'conditions': [ - ['target_arch == "x64"', { + ['target_arch == "ia32" or target_arch == "x86"', { + 'variables': { + 'libffi_arch_sources': [ + 'src/x86/ffi.c', + 'src/x86/sysv.S', + ], + }, + }], + ['target_arch == "x64" or target_arch == "x86_64"', { 'variables': { 'libffi_arch_sources': [ 'src/x86/ffiw64.c', @@ -67,25 +81,77 @@ ], }, }], - ], - }], - ['OS == "mac"', { - 'conditions': [ - ['target_arch == "x64"', { + ['target_arch == "s390x"', { 'variables': { 'libffi_arch_sources': [ - 'src/x86/ffiw64.c', - 'src/x86/ffi64.c', - 'src/x86/unix64.S', - 'src/x86/win64.S', + 'src/s390/ffi.c', + 'src/s390/sysv.S', ], }, }], - ['target_arch == "arm64"', { + ['target_arch == "riscv64"', { 'variables': { 'libffi_arch_sources': [ - 'src/aarch64/ffi.c', - 'src/aarch64/sysv.S', + 'src/riscv/ffi.c', + 'src/riscv/sysv.S', + ], + }, + }], + ['target_arch == "loong64"', { + 'variables': { + 'libffi_arch_sources': [ + 'src/loongarch64/ffi.c', + 'src/loongarch64/sysv.S', + ], + }, + }], + ['target_arch == "mips" or target_arch == "mipsel" or target_arch == "mips64el"', { + 'variables': { + 'libffi_arch_sources': [ + 'src/mips/ffi.c', + 'src/mips/o32.S', + 'src/mips/n32.S', + ], + }, + }], + ['target_arch == "ppc64" and OS == "mac"', { + 'variables': { + 'libffi_arch_sources': [ + 'src/powerpc/ffi_darwin.c', + 'src/powerpc/darwin.S', + 'src/powerpc/darwin_closure.S', + ], + }, + }], + ['target_arch == "ppc64" and OS == "aix"', { + 'variables': { + 'libffi_arch_sources': [ + 'src/powerpc/ffi_darwin.c', + 'src/powerpc/aix.S', + 'src/powerpc/aix_closure.S', + ], + }, + }], + ['target_arch == "ppc64" and (OS == "freebsd" or OS == "openbsd")', { + 'variables': { + 'libffi_arch_sources': [ + 'src/powerpc/ffi.c', + 'src/powerpc/ffi_sysv.c', + 'src/powerpc/sysv.S', + 'src/powerpc/ppc_closure.S', + ], + }, + }], + ['target_arch == "ppc64" and OS == "linux"', { + 'variables': { + 'libffi_arch_sources': [ + 'src/powerpc/ffi.c', + 'src/powerpc/ffi_sysv.c', + 'src/powerpc/ffi_linux64.c', + 'src/powerpc/sysv.S', + 'src/powerpc/ppc_closure.S', + 'src/powerpc/linux64.S', + 'src/powerpc/linux64_closure.S', ], }, }], @@ -121,6 +187,11 @@ 'include/ffi.h.in', 'src/aarch64/ffitarget.h', 'src/arm/ffitarget.h', + 'src/loongarch64/ffitarget.h', + 'src/mips/ffitarget.h', + 'src/powerpc/ffitarget.h', + 'src/riscv/ffitarget.h', + 'src/s390/ffitarget.h', 'src/x86/ffitarget.h', ], 'outputs': [ @@ -141,7 +212,7 @@ }, ], 'conditions': [ - ['OS == "win" and target_arch == "x64"', { + ['OS == "win" and (target_arch == "x64" or target_arch == "x86_64")', { 'actions': [ { 'action_name': 'preprocess_win64_intel_asm', @@ -176,6 +247,75 @@ }, ], }], + ['OS == "win" and (target_arch == "ia32" or target_arch == "x86")', { + 'actions': [ + { + 'action_name': 'preprocess_win32_intel_asm', + 'process_outputs_as_sources': 1, + 'inputs': [ + 'preprocess_asm.py', + 'include/ffi_cfi.h', + 'src/x86/asmnames.h', + 'src/x86/sysv_intel.S', + '<(SHARED_INTERMEDIATE_DIR)/libffi/ffi.h', + '<(SHARED_INTERMEDIATE_DIR)/libffi/fficonfig.h', + ], + 'outputs': [ + '<(INTERMEDIATE_DIR)/sysv_intel.asm', + ], + 'action': [ + '<(python)', + 'preprocess_asm.py', + '--input', + 'src/x86/sysv_intel.S', + '--output', + '<@(_outputs)', + '--include-dir', + 'include', + '--include-dir', + 'src/x86', + '--include-dir', + '<(SHARED_INTERMEDIATE_DIR)/libffi', + '--define', + 'FFI_STATIC_BUILD', + ], + }, + ], + }], + ['OS == "win" and target_arch == "arm"', { + 'actions': [ + { + 'action_name': 'preprocess_win32_arm_asm', + 'process_outputs_as_sources': 1, + 'inputs': [ + 'preprocess_asm.py', + 'include/ffi_cfi.h', + 'src/arm/sysv_msvc_arm32.S', + '<(SHARED_INTERMEDIATE_DIR)/libffi/ffi.h', + '<(SHARED_INTERMEDIATE_DIR)/libffi/fficonfig.h', + ], + 'outputs': [ + '<(INTERMEDIATE_DIR)/sysv_msvc_arm32.asm', + ], + 'action': [ + '<(python)', + 'preprocess_asm.py', + '--input', + 'src/arm/sysv_msvc_arm32.S', + '--output', + '<@(_outputs)', + '--include-dir', + 'include', + '--include-dir', + 'src/arm', + '--include-dir', + '<(SHARED_INTERMEDIATE_DIR)/libffi', + '--define', + 'FFI_STATIC_BUILD', + ], + }, + ], + }], ['OS == "win" and target_arch == "arm64"', { # Link the prebuilt object file directly 'libraries': [ diff --git a/doc/api/ffi.md b/doc/api/ffi.md index 382b95fa07c3e3..3adc6d4476790e 100644 --- a/doc/api/ffi.md +++ b/doc/api/ffi.md @@ -30,15 +30,16 @@ const ffi = require('node:ffi'); This module is only available under the `node:` scheme in builds with FFI support and is gated by the `--experimental-ffi` flag. -Bundled libffi support currently targets: +Bundled libffi support is available on all Node.js supported platforms where +libffi provides a compatible static backend. Targets not supported by bundled +libffi require building Node.js against a shared libffi with `--shared-ffi`. +The unofficial GN build does not support `node:ffi`. -* macOS on `arm64` and `x64` -* Windows on `arm64` and `x64` -* FreeBSD on `arm`, `arm64`, and `x64` -* Linux on `arm`, `arm64`, and `x64` +The following targets are not supported by bundled libffi: -Other targets require building Node.js against a shared libffi with -`--shared-ffi`. The unofficial GN build does not support `node:ffi`. +* `mips`, `mipsel`, and `mips64el` on targets other than FreeBSD, Linux, and + OpenBSD. +* `ppc64` on Android, CloudABI, iOS, OpenHarmony, OS/400, Solaris, and Windows. When using the [Permission Model][], FFI APIs are restricted unless the [`--allow-ffi`][] flag is provided. From 2f89df22aa2892f6c5bcd1dc3e2bb1c4bbbc9b0c Mon Sep 17 00:00:00 2001 From: Paolo Insogna Date: Fri, 8 May 2026 08:32:54 +0200 Subject: [PATCH 2/5] fixup Signed-off-by: Paolo Insogna --- deps/libffi/libffi.gyp | 1 + deps/libffi/src/s390/sysv.S | 54 ++++++++++++++-------------- test/ffi/ffi.status | 6 ++++ test/ffi/fixture_library/binding.gyp | 6 ++++ 4 files changed, 40 insertions(+), 27 deletions(-) diff --git a/deps/libffi/libffi.gyp b/deps/libffi/libffi.gyp index 2d6cccd51a3097..76032835b6bd54 100644 --- a/deps/libffi/libffi.gyp +++ b/deps/libffi/libffi.gyp @@ -173,6 +173,7 @@ ], 'include_dirs': [ 'include', + 'src', '<(SHARED_INTERMEDIATE_DIR)/libffi', ], 'sources': [ diff --git a/deps/libffi/src/s390/sysv.S b/deps/libffi/src/s390/sysv.S index d603218e6e200d..b6c63f30f2bb39 100644 --- a/deps/libffi/src/s390/sysv.S +++ b/deps/libffi/src/s390/sysv.S @@ -50,11 +50,11 @@ ffi_call_SYSV: st %r6,44(%r2) # Save registers stm %r12,%r14,48(%r2) lr %r13,%r2 # Install frame pointer - .cfi_rel_offset r6, 44 - .cfi_rel_offset r12, 48 - .cfi_rel_offset r13, 52 - .cfi_rel_offset r14, 56 - .cfi_def_cfa_register r13 + .cfi_rel_offset %r6, 44 + .cfi_rel_offset %r12, 48 + .cfi_rel_offset %r13, 52 + .cfi_rel_offset %r14, 56 + .cfi_def_cfa_register %r13 st %r2,0(%r15) # Set up back chain sla %r3,3 # ret_type *= 8 lr %r12,%r4 # Save ret_addr @@ -142,12 +142,12 @@ ffi_closure_SYSV: .Ldoclosure: stm %r12,%r15,48(%r15) # Save registers lr %r12,%r15 - .cfi_def_cfa_register r12 - .cfi_rel_offset r6, 24 - .cfi_rel_offset r12, 48 - .cfi_rel_offset r13, 52 - .cfi_rel_offset r14, 56 - .cfi_rel_offset r15, 60 + .cfi_def_cfa_register %r12 + .cfi_rel_offset %r6, 24 + .cfi_rel_offset %r12, 48 + .cfi_rel_offset %r13, 52 + .cfi_rel_offset %r14, 56 + .cfi_rel_offset %r15, 60 #ifndef HAVE_AS_S390_ZARCH basr %r13,0 # Set up base register .Lcbase: @@ -170,7 +170,7 @@ ffi_closure_SYSV: #endif lr %r15,%r12 - .cfi_def_cfa_register r15 + .cfi_def_cfa_register %r15 lm %r12,%r14,48(%r12) # Restore saved registers l %r6,24(%r15) ld %f0,64(%r15) # Load return registers @@ -204,11 +204,11 @@ ffi_call_SYSV: stg %r6,88(%r2) # Save registers stmg %r12,%r14,96(%r2) lgr %r13,%r2 # Install frame pointer - .cfi_rel_offset r6, 88 - .cfi_rel_offset r12, 96 - .cfi_rel_offset r13, 104 - .cfi_rel_offset r14, 112 - .cfi_def_cfa_register r13 + .cfi_rel_offset %r6, 88 + .cfi_rel_offset %r12, 96 + .cfi_rel_offset %r13, 104 + .cfi_rel_offset %r14, 112 + .cfi_def_cfa_register %r13 stg %r2,0(%r15) # Set up back chain larl %r14,.Ltable # Set up return address slag %r3,%r3,3 # ret_type *= 8 @@ -253,10 +253,10 @@ ffi_call_SYSV: lg %r12,96(%r13) lg %r6,88(%r13) lg %r13,104(%r13) - .cfi_restore r14 - .cfi_restore r13 - .cfi_restore r12 - .cfi_restore r6 + .cfi_restore %r14 + .cfi_restore %r13 + .cfi_restore %r12 + .cfi_restore %r6 .cfi_def_cfa r15, 160 br %r14 .cfi_endproc @@ -292,11 +292,11 @@ ffi_closure_SYSV: .Ldoclosure: stmg %r13,%r15,104(%r15) # Save registers lgr %r13,%r15 - .cfi_def_cfa_register r13 - .cfi_rel_offset r6, 48 - .cfi_rel_offset r13, 104 - .cfi_rel_offset r14, 112 - .cfi_rel_offset r15, 120 + .cfi_def_cfa_register %r13 + .cfi_rel_offset %r6, 48 + .cfi_rel_offset %r13, 104 + .cfi_rel_offset %r14, 112 + .cfi_rel_offset %r15, 120 aghi %r15,-160-16 # Set up stack frame stg %r13,0(%r15) # Set up back chain @@ -311,7 +311,7 @@ ffi_closure_SYSV: brasl %r14,ffi_closure_helper_SYSV # Call helper lgr %r15,%r13 - .cfi_def_cfa_register r15 + .cfi_def_cfa_register %r15 lmg %r13,%r14,104(%r13) # Restore saved registers lg %r6,48(%r15) ld %f0,128(%r15) # Load return registers diff --git a/test/ffi/ffi.status b/test/ffi/ffi.status index 8c917cfebadc59..a5274bfaf4af48 100644 --- a/test/ffi/ffi.status +++ b/test/ffi/ffi.status @@ -1,3 +1,9 @@ prefix ffi [true] # This section applies to all platforms + +[$system==solaris] # Also applies to SmartOS +# Bundled libffi callbacks crash on SmartOS. +test-ffi-calls: SKIP +test-ffi-shared-buffer: SKIP +test-ffi-weakref-calls: SKIP diff --git a/test/ffi/fixture_library/binding.gyp b/test/ffi/fixture_library/binding.gyp index ede9e9416da4f6..90b1ede016629f 100644 --- a/test/ffi/fixture_library/binding.gyp +++ b/test/ffi/fixture_library/binding.gyp @@ -4,6 +4,12 @@ 'target_name': 'ffi_test_library', 'sources': ['ffi_test_library.c'], 'type': 'shared_library', + 'conditions': [ + ['OS in "aix os400"', { + 'product_extension': 'so', + 'ldflags': [ '-Wl,-G' ], + }], + ], } ] } From c1aa2ba6952a6b8926c8acc6596cdf50715532a0 Mon Sep 17 00:00:00 2001 From: Paolo Insogna Date: Fri, 8 May 2026 11:50:54 +0200 Subject: [PATCH 3/5] fixup Signed-off-by: Paolo Insogna --- src/ffi/types.cc | 4 ++-- test/ffi/test-ffi-dynamic-library.js | 8 ++++++-- test/ffi/test-ffi-memory.js | 2 +- test/ffi/test-ffi-shared-buffer.js | 5 +++++ 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/ffi/types.cc b/src/ffi/types.cc index 4d5062cdf832fb..e8469ebc0bbcc6 100644 --- a/src/ffi/types.cc +++ b/src/ffi/types.cc @@ -667,7 +667,7 @@ bool ToFFIReturnValue(Local result, ffi_type* type, void* ret) { return false; } - *static_cast(ret) = static_cast(value); + *static_cast(ret) = static_cast(value); } else if (type == &ffi_type_uint32) { uint64_t value; @@ -675,7 +675,7 @@ bool ToFFIReturnValue(Local result, ffi_type* type, void* ret) { return false; } - *static_cast(ret) = static_cast(value); + *static_cast(ret) = static_cast(value); } else if (type == &ffi_type_sint64) { bool lossless; diff --git a/test/ffi/test-ffi-dynamic-library.js b/test/ffi/test-ffi-dynamic-library.js index ebe3af80586b45..e3171b57124250 100644 --- a/test/ffi/test-ffi-dynamic-library.js +++ b/test/ffi/test-ffi-dynamic-library.js @@ -4,6 +4,7 @@ const common = require('../common'); common.skipIfFFIMissing(); const { gcUntil } = require('../common/gc'); const assert = require('node:assert'); +const { endianness } = require('node:os'); const { test } = require('node:test'); const ffi = require('node:ffi'); const { fixtureSymbols, libraryPath } = require('./ffi-test-common'); @@ -54,8 +55,11 @@ test('dlopen resolves functions from definitions', () => { // Shared-buffer wrapper sets `length` to the FFI signature's arity // (see `inheritMetadata` in lib/internal/ffi-shared-buffer.js). The raw // native function has length 0, but the wrapper exposes the parameter - // count so `fn.length` is useful for introspection. - assert.strictEqual(functions.add_i32.length, 2); + // count so `fn.length` is useful for introspection. The shared-buffer + // wrapper is disabled on big-endian hosts. + assert.strictEqual( + functions.add_i32.length, + endianness() === 'BE' ? 0 : 2); assert.strictEqual(typeof functions.add_i32.pointer, 'bigint'); assert.strictEqual(Object.getPrototypeOf(functions), null); } finally { diff --git a/test/ffi/test-ffi-memory.js b/test/ffi/test-ffi-memory.js index 5a6667fa512bcb..adc5a8539491d5 100644 --- a/test/ffi/test-ffi-memory.js +++ b/test/ffi/test-ffi-memory.js @@ -177,7 +177,7 @@ test('ffi exportString and exportBuffer copy data into native memory', () => { const viewSource = new Uint16Array([0x0102, 0x0304, 0x0506]); const middleBytes = new Uint8Array(viewSource.buffer, 2, 2); ffi.exportArrayBufferView(middleBytes, viewPtr, 2); - assert.deepStrictEqual([...ffi.toBuffer(viewPtr, 2)], [0x04, 0x03]); + assert.deepStrictEqual([...ffi.toBuffer(viewPtr, 2)], [...middleBytes]); const bufferViewPtr = alloc(8); const bufferView = Buffer.from([1, 7, 2, 8, 3]); diff --git a/test/ffi/test-ffi-shared-buffer.js b/test/ffi/test-ffi-shared-buffer.js index 43d76a4da18315..944b4021abc47a 100644 --- a/test/ffi/test-ffi-shared-buffer.js +++ b/test/ffi/test-ffi-shared-buffer.js @@ -4,8 +4,13 @@ const common = require('../common'); common.skipIfFFIMissing(); const assert = require('node:assert'); +const { endianness } = require('node:os'); const { test } = require('node:test'); +if (endianness() === 'BE') { + common.skip('shared-buffer FFI is disabled on big-endian hosts'); +} + // Capture the unpatched DynamicLibrary.prototype.getFunction BEFORE loading // `node:ffi`, which patches it. The SB-metadata test below uses the raw // method to inspect Symbol-keyed internals that `inheritMetadata` From 262ee190b52f63d0a549d9fd796a91f84a15e4d9 Mon Sep 17 00:00:00 2001 From: Paolo Insogna Date: Fri, 8 May 2026 17:54:14 +0200 Subject: [PATCH 4/5] fixup --- configure.py | 2 +- deps/libffi/generate-headers.py | 4 --- deps/libffi/libffi.gyp | 9 ------ deps/libffi/src/s390/sysv.S | 54 ++++++++++++++++----------------- doc/api/ffi.md | 1 + 5 files changed, 29 insertions(+), 41 deletions(-) diff --git a/configure.py b/configure.py index 50200d5b87437d..eea76312119385 100755 --- a/configure.py +++ b/configure.py @@ -2314,7 +2314,7 @@ def bundled_ffi_supported(os_name, target_arch): target_arch = 'ia32' if target_arch in {'arm', 'arm64', 'ia32', 'x64', 'x86_64', - 's390x', 'riscv64', 'loong64'}: + 'riscv64', 'loong64'}: return True if target_arch in {'mips', 'mipsel', 'mips64el'}: diff --git a/deps/libffi/generate-headers.py b/deps/libffi/generate-headers.py index ef91278f5b9fd3..d1b024450caba1 100644 --- a/deps/libffi/generate-headers.py +++ b/deps/libffi/generate-headers.py @@ -41,9 +41,6 @@ def get_target(os_name, target_arch): if target_arch == 'x64': return ('X86_WIN64' if os_name == 'win' else 'X86_64', 'x86') - if target_arch == 's390x': - return ('S390', 's390') - if target_arch == 'riscv64': return ('RISCV', 'riscv') @@ -241,7 +238,6 @@ def detect_target_arch(): 'arm64': 'arm64', 'aarch64': 'arm64', 'arm': 'arm', - 's390x': 's390x', 'riscv64': 'riscv64', 'loong64': 'loong64', 'ppc64': 'ppc64', diff --git a/deps/libffi/libffi.gyp b/deps/libffi/libffi.gyp index 76032835b6bd54..e02e959c19a001 100644 --- a/deps/libffi/libffi.gyp +++ b/deps/libffi/libffi.gyp @@ -81,14 +81,6 @@ ], }, }], - ['target_arch == "s390x"', { - 'variables': { - 'libffi_arch_sources': [ - 'src/s390/ffi.c', - 'src/s390/sysv.S', - ], - }, - }], ['target_arch == "riscv64"', { 'variables': { 'libffi_arch_sources': [ @@ -192,7 +184,6 @@ 'src/mips/ffitarget.h', 'src/powerpc/ffitarget.h', 'src/riscv/ffitarget.h', - 'src/s390/ffitarget.h', 'src/x86/ffitarget.h', ], 'outputs': [ diff --git a/deps/libffi/src/s390/sysv.S b/deps/libffi/src/s390/sysv.S index b6c63f30f2bb39..d603218e6e200d 100644 --- a/deps/libffi/src/s390/sysv.S +++ b/deps/libffi/src/s390/sysv.S @@ -50,11 +50,11 @@ ffi_call_SYSV: st %r6,44(%r2) # Save registers stm %r12,%r14,48(%r2) lr %r13,%r2 # Install frame pointer - .cfi_rel_offset %r6, 44 - .cfi_rel_offset %r12, 48 - .cfi_rel_offset %r13, 52 - .cfi_rel_offset %r14, 56 - .cfi_def_cfa_register %r13 + .cfi_rel_offset r6, 44 + .cfi_rel_offset r12, 48 + .cfi_rel_offset r13, 52 + .cfi_rel_offset r14, 56 + .cfi_def_cfa_register r13 st %r2,0(%r15) # Set up back chain sla %r3,3 # ret_type *= 8 lr %r12,%r4 # Save ret_addr @@ -142,12 +142,12 @@ ffi_closure_SYSV: .Ldoclosure: stm %r12,%r15,48(%r15) # Save registers lr %r12,%r15 - .cfi_def_cfa_register %r12 - .cfi_rel_offset %r6, 24 - .cfi_rel_offset %r12, 48 - .cfi_rel_offset %r13, 52 - .cfi_rel_offset %r14, 56 - .cfi_rel_offset %r15, 60 + .cfi_def_cfa_register r12 + .cfi_rel_offset r6, 24 + .cfi_rel_offset r12, 48 + .cfi_rel_offset r13, 52 + .cfi_rel_offset r14, 56 + .cfi_rel_offset r15, 60 #ifndef HAVE_AS_S390_ZARCH basr %r13,0 # Set up base register .Lcbase: @@ -170,7 +170,7 @@ ffi_closure_SYSV: #endif lr %r15,%r12 - .cfi_def_cfa_register %r15 + .cfi_def_cfa_register r15 lm %r12,%r14,48(%r12) # Restore saved registers l %r6,24(%r15) ld %f0,64(%r15) # Load return registers @@ -204,11 +204,11 @@ ffi_call_SYSV: stg %r6,88(%r2) # Save registers stmg %r12,%r14,96(%r2) lgr %r13,%r2 # Install frame pointer - .cfi_rel_offset %r6, 88 - .cfi_rel_offset %r12, 96 - .cfi_rel_offset %r13, 104 - .cfi_rel_offset %r14, 112 - .cfi_def_cfa_register %r13 + .cfi_rel_offset r6, 88 + .cfi_rel_offset r12, 96 + .cfi_rel_offset r13, 104 + .cfi_rel_offset r14, 112 + .cfi_def_cfa_register r13 stg %r2,0(%r15) # Set up back chain larl %r14,.Ltable # Set up return address slag %r3,%r3,3 # ret_type *= 8 @@ -253,10 +253,10 @@ ffi_call_SYSV: lg %r12,96(%r13) lg %r6,88(%r13) lg %r13,104(%r13) - .cfi_restore %r14 - .cfi_restore %r13 - .cfi_restore %r12 - .cfi_restore %r6 + .cfi_restore r14 + .cfi_restore r13 + .cfi_restore r12 + .cfi_restore r6 .cfi_def_cfa r15, 160 br %r14 .cfi_endproc @@ -292,11 +292,11 @@ ffi_closure_SYSV: .Ldoclosure: stmg %r13,%r15,104(%r15) # Save registers lgr %r13,%r15 - .cfi_def_cfa_register %r13 - .cfi_rel_offset %r6, 48 - .cfi_rel_offset %r13, 104 - .cfi_rel_offset %r14, 112 - .cfi_rel_offset %r15, 120 + .cfi_def_cfa_register r13 + .cfi_rel_offset r6, 48 + .cfi_rel_offset r13, 104 + .cfi_rel_offset r14, 112 + .cfi_rel_offset r15, 120 aghi %r15,-160-16 # Set up stack frame stg %r13,0(%r15) # Set up back chain @@ -311,7 +311,7 @@ ffi_closure_SYSV: brasl %r14,ffi_closure_helper_SYSV # Call helper lgr %r15,%r13 - .cfi_def_cfa_register %r15 + .cfi_def_cfa_register r15 lmg %r13,%r14,104(%r13) # Restore saved registers lg %r6,48(%r15) ld %f0,128(%r15) # Load return registers diff --git a/doc/api/ffi.md b/doc/api/ffi.md index 3adc6d4476790e..61307eb16da899 100644 --- a/doc/api/ffi.md +++ b/doc/api/ffi.md @@ -37,6 +37,7 @@ The unofficial GN build does not support `node:ffi`. The following targets are not supported by bundled libffi: +* `s390x`. * `mips`, `mipsel`, and `mips64el` on targets other than FreeBSD, Linux, and OpenBSD. * `ppc64` on Android, CloudABI, iOS, OpenHarmony, OS/400, Solaris, and Windows. From 82310620f4372c92bd0aae18982095ec098818cd Mon Sep 17 00:00:00 2001 From: Paolo Insogna Date: Sat, 9 May 2026 06:15:26 +0200 Subject: [PATCH 5/5] fixup --- doc/api/ffi.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/api/ffi.md b/doc/api/ffi.md index 61307eb16da899..37ccf2a719bc1a 100644 --- a/doc/api/ffi.md +++ b/doc/api/ffi.md @@ -30,9 +30,9 @@ const ffi = require('node:ffi'); This module is only available under the `node:` scheme in builds with FFI support and is gated by the `--experimental-ffi` flag. -Bundled libffi support is available on all Node.js supported platforms where -libffi provides a compatible static backend. Targets not supported by bundled -libffi require building Node.js against a shared libffi with `--shared-ffi`. +Building Node.js with `node:ffi` support is available via the bundled `libffi` on +platforms where `libffi` provides a compatible static backend, or via a +shared `libffi` using the `--shared-ffi` configure flag. The unofficial GN build does not support `node:ffi`. The following targets are not supported by bundled libffi: