FFI | Node.js v26.8.2 Documentation
https://nodejs.org/api/ffi.html • 96 KB fetched
Open original page
FFI | Node.js v26.8.2 Documentation Skip to content
Node.js
* About this documentation
* Usage and example
* Assertion testing
* Asynchronous context tracking
* Async hooks
* Buffer
* C++ addons
* C/C++ addons with Node-API
* C++ embedder API
* Child processes
* Cluster
* Command-line options
* Console
* Crypto
* Debugger
* Deprecated APIs
* Diagnostics Channel
* DNS
* Domain
* Environment Variables
* Errors
* Events
* File system
* FFI
* Globals
* HTTP
* HTTP/2
* HTTPS
* Inspector
* Internationalization
* Iterable Streams API
* Modules: CommonJS modules
* Modules: ECMAScript modules
* Modules: node:module API
* Modules: Packages
* Modules: TypeScript
* Net
* OS
* Path
* Performance hooks
* Permissions
* Process
* Punycode
* Query strings
* Readline
* REPL
* Report
* Single executable applications
* SQLite
* Stream
* String decoder
* Test runner
* Timers
* TLS/SSL
* Trace events
* TTY
* UDP/datagram
* URL
* Utilities
* V8
* Virtual File System
* VM
* WASI
* Web Crypto API
* Web Streams API
* Worker threads
* Zlib
*
Code repository and issue tracker
Node.js v26.8.2 documentation
* Node.js v26.8.2
* Table of contents
* FFI
* Overview
* Type names
* Signature objects
* ffi.suffix
* ffi.dlopen(path[, definitions])
* ffi.dlclose(handle)
* ffi.dlsym(handle, symbol)
* Class: DynamicLibrary
* new DynamicLibrary(path)
* library.path
* library.functions
* library.symbols
* library.close()
* library[Symbol.dispose]()
* library.getFunction(name, signature)
* library.getFunctions([definitions])
* library.getSymbol(name)
* library.getSymbols()
* library.registerCallback([signature,] callback)
* library.unregisterCallback(pointer)
* library.refCallback(pointer)
* library.unrefCallback(pointer)
* Calling native functions
* Primitive memory access helpers
* ffi.toString(pointer)
* ffi.toBuffer(pointer, length[, copy])
* ffi.toArrayBuffer(pointer, length[, copy])
* ffi.exportString(string, pointer, length[, encoding])
* ffi.exportBuffer(buffer, pointer, length)
* ffi.exportArrayBuffer(arrayBuffer, pointer, length)
* ffi.exportArrayBufferView(arrayBufferView, pointer, length)
* ffi.getRawPointer(source)
* ffi.getCurrentEventLoop()
* Safety notes
* Index
* Index
* About this documentation
* Usage and example
* Assertion testing
* Asynchronous context tracking
* Async hooks
* Buffer
* C++ addons
* C/C++ addons with Node-API
* C++ embedder API
* Child processes
* Cluster
* Command-line options
* Console
* Crypto
* Debugger
* Deprecated APIs
* Diagnostics Channel
* DNS
* Domain
* Environment Variables
* Errors
* Events
* File system
* FFI
* Globals
* HTTP
* HTTP/2
* HTTPS
* Inspector
* Internationalization
* Iterable Streams API
* Modules: CommonJS modules
* Modules: ECMAScript modules
* Modules: node:module API
* Modules: Packages
* Modules: TypeScript
* Net
* OS
* Path
* Performance hooks
* Permissions
* Process
* Punycode
* Query strings
* Readline
* REPL
* Report
* Single executable applications
* SQLite
* Stream
* String decoder
* Test runner
* Timers
* TLS/SSL
* Trace events
* TTY
* UDP/datagram
* URL
* Utilities
* V8
* Virtual File System
* VM
* WASI
* Web Crypto API
* Web Streams API
* Worker threads
* Zlib
* Other versions
* 26.x
*
Options
*
View on single page
*
View as JSON
* Edit on GitHub
Table of contents
* FFI
* Overview
* Type names
* Signature objects
* ffi.suffix
* ffi.dlopen(path[, definitions])
* ffi.dlclose(handle)
* ffi.dlsym(handle, symbol)
* Class: DynamicLibrary
* new DynamicLibrary(path)
* library.path
* library.functions
* library.symbols
* library.close()
* library[Symbol.dispose]()
* library.getFunction(name, signature)
* library.getFunctions([definitions])
* library.getSymbol(name)
* library.getSymbols()
* library.registerCallback([signature,] callback)
* library.unregisterCallback(pointer)
* library.refCallback(pointer)
* library.unrefCallback(pointer)
* Calling native functions
* Primitive memory access helpers
* ffi.toString(pointer)
* ffi.toBuffer(pointer, length[, copy])
* ffi.toArrayBuffer(pointer, length[, copy])
* ffi.exportString(string, pointer, length[, encoding])
* ffi.exportBuffer(buffer, pointer, length)
* ffi.exportArrayBuffer(arrayBuffer, pointer, length)
* ffi.exportArrayBufferView(arrayBufferView, pointer, length)
* ffi.getRawPointer(source)
* ffi.getCurrentEventLoop()
* Safety notes
FFI #
Source Code: lib/ffi.js Added in: v26.1.0
Stability: 1 - Experimental
The node:ffi module provides an experimental foreign function interface for
loading dynamic libraries and calling native symbols from JavaScript. This API is unsafe. Passing invalid pointers, using an incorrect symbol
signature, or accessing memory after it has been freed can crash the process
or corrupt memory. To access it: import ffi from 'node:ffi' ;
const ffi = require ( 'node:ffi' ) ;
javascript copy
This module is only available under the node: scheme in builds with FFI
support and is gated by the --experimental-ffi flag. 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:
* s390x .
* 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.
Overview #
The node:ffi module exposes two groups of APIs:
* Dynamic library APIs for loading libraries, resolving symbols, and creating
callable JavaScript wrappers.
* Raw memory helpers for reading and writing primitive values through pointers,
converting pointers to JavaScript strings, Buffer instances, and
ArrayBuffer instances, and for copying data back into native memory.
Type names #
FFI signatures use string type names. Supported type names:
* void
* char
* int8
* uint8
* int16
* uint16
* int32
* uint32
* int64
* uint64
* float32
* float64
* pointer
* string
* buffer
* arraybuffer
* function
Alternative spellings
* i8 for int8
* u8 and bool for uint8
* i16 for int16
* u16 for uint16
* i32 for int32
* u32 for uint32
* i64 for int64
* u64 for uint64
* f32 and float for float32
* f64 and double for float64
* ptr for pointer
* str for string
These type names are also exposed as constants on ffi.types :
* ffi.types.VOID = 'void'
* ffi.types.POINTER = 'pointer'
* ffi.types.BUFFER = 'buffer'
* ffi.types.ARRAY_BUFFER = 'arraybuffer'
* ffi.types.FUNCTION = 'function'
* ffi.types.BOOL = 'bool'
* ffi.types.CHAR = 'char'
* ffi.types.STRING = 'string'
* ffi.types.FLOAT = 'float'
* ffi.types.DOUBLE = 'double'
* ffi.types.INT_8 = 'int8'
* ffi.types.UINT_8 = 'uint8'
* ffi.types.INT_16 = 'int16'
* ffi.types.UINT_16 = 'uint16'
* ffi.types.INT_32 = 'int32'
* ffi.types.UINT_32 = 'uint32'
* ffi.types.INT_64 = 'int64'
* ffi.types.UINT_64 = 'uint64'
* ffi.types.FLOAT_32 = 'float32'
* ffi.types.FLOAT_64 = 'float64'
Pointer-like types ( pointer , string , buffer , arraybuffer , and
function ) are all passed through the native layer as pointers. When Buffer , ArrayBuffer , or typed array values are passed as pointer-like
arguments, Node.js borrows a raw pointer to their backing memory for the
duration of the native call. The caller must ensure that backing store remains
valid and stable for the entire call. It is unsupported and dangerous to resize, transfer, detach, or otherwise
invalidate that backing store while the native call is active, including
through reentrant JavaScript such as FFI callbacks. Doing so may crash the
process, produce incorrect output, or corrupt memory. The char type follows the platform C ABI. On platforms where plain C char
is signed it behaves like int8 ; otherwise it behaves like uint8 . The bool type is marshaled as an 8-bit unsigned integer. Pass numeric values
such as 0 and 1 ; JavaScript true and false are not accepted. On optimized Fast FFI calls, pointer and function parameters accept raw
pointer bigint values. For pointer-like parameters, null , undefined ,
strings, Buffer , typed array, DataView , and ArrayBuffer values are converted
on the JavaScript side before calling the optimized native wrapper. Optimized Fast FFI calls fall back to the generic FFI call path when a
function's arguments or return type do not fit the platform-specific fast
trampoline. Fast FFI calls support at most 8 total arguments, and the
register and argument limits differ per architecture:
Architecture
Max integer/pointer args
Max floating-point args
Buffer-shaped args
Buffer-shaped + FP together
Narrow (8/16-bit) return
AArch64
7 (6 when a buffer-shaped arg is present)
8
Supported
Not supported
Supported
x86-64, Linux/macOS (SysV)
6 (4 when a buffer-shaped arg is present)
8
Supported
Not supported
Supported
x86-64, Windows (Win64)
3 (total arguments also capped at 3)
3
Not supported
N/A
Supported
s390x
4
4
Not supported
N/A
Not supported
PPC64LE
7
8
Not supported
N/A
Not supported
LoongArch64
7
8
Not supported
N/A
Not supported
RISC-V (64-bit)
7
8
Not supported
N/A
Not supported
PPC64BE has no fast-call trampoline and always uses the generic call path.
"Buffer-shaped args" means Buffer , typed array, DataView , or ArrayBuffer
values passed as pointer-like arguments. Functions whose argument or return
types exceed the limits for the current platform use the generic FFI call
path instead.
Signature objects #
Functions and callbacks are described with signature objects. Signature objects may contain the following properties, both of which are
optional:
* return <string> A type name specifying the return type of the
function or callback. Default: 'void' .
* arguments <string> [] An array of type names specifying the argument
type list of the function or callback. Default: [] .
const signature = {
return : 'int32' ,
arguments : [ 'int32' , 'int32' ] ,
};
js copy
ffi.suffix #
Added in: v26.1.0
* <string>
The native shared library suffix for the current platform:
* 'dylib' on macOS
* 'so' on Unix-like platforms
* 'dll' on Windows
This can be used to build portable library paths: const { suffix } = require ( 'node:ffi' ) ;
const path = `libsqlite3. ${ suffix } ` ;
cjs copy
ffi.dlopen(path[, definitions]) #
Added in: v26.1.0
* path <string> | <null> Path to a dynamic library, or null to resolve symbols
from the current process image.
* definitions <Object> Symbol definitions to resolve immediately.
* Returns: <Object>
Loads a dynamic library and resolves the requested function definitions. On Windows passing null is not supported. When definitions is omitted, functions is returned as an empty object until
symbols are resolved explicitly. The returned object contains:
* lib <DynamicLibrary> The loaded library handle.
* functions <Object> Callable wrappers for the requested symbols.
The returned object also implements the explicit resource management protocol,
so it can be used with the using declaration. Disposing the returned
object closes the library handle. import { dlopen , suffix } from 'node:ffi' ;
{
using handle = dlopen ( `./mylib. ${ suffix } ` , {
add_i32 : { arguments : [ 'int32' , 'int32' ] , return : 'int32' },
} ) ;
console . log (handle . functions . add_i32 ( 20 , 22 )) ;
} // handle.lib.close() is invoked automatically here.
mjs copy
import { dlopen , suffix } from 'node:ffi' ;
const { lib , functions } = dlopen ( `./mylib. ${ suffix } ` , {
add_i32 : { arguments : [ 'int32' , 'int32' ] , return : 'int32' },
string_length : { arguments : [ 'pointer' ] , return : 'uint64' },
} ) ;
console . log (functions . add_i32 ( 20 , 22 )) ;
const { dlopen , suffix } = require ( 'node:ffi' ) ;
const { lib , functions } = dlopen ( `./mylib. ${ suffix } ` , {
add_i32 : { arguments : [ 'int32' , 'int32' ] , return : 'int32' },
string_length : { arguments : [ 'pointer' ] , return : 'uint64' },
} ) ;
console . log (functions . add_i32 ( 20 , 22 )) ;
javascript copy
ffi.dlclose(handle) #
Added in: v26.1.0
* handle <DynamicLibrary>
Closes a dynamic library. This is equivalent to calling handle.close() .
ffi.dlsym(handle, symbol) #
Added in: v26.1.0
* handle <DynamicLibrary>
* symbol <string>
* Returns: <bigint>
Resolves a symbol address from a loaded library. This is equivalent to calling handle.getSymbol(symbol) .
Class: DynamicLibrary #
Added in: v26.1.0
Represents a loaded dynamic library.
new DynamicLibrary(path) #
* path <string> | <null> Path to a dynamic library, or null to resolve symbols
from the current process image.
Loads the dynamic library without resolving any functions eagerly. On Windows passing null is not supported. const { DynamicLibrary , suffix } = require ( 'node:ffi' ) ;
const lib = new DynamicLibrary ( `./mylib. ${ suffix } ` ) ;
cjs copy
library.path #
* <string>
The path used to load the library.
library.functions #
* <Object>
An object containing previously resolved function wrappers.
library.symbols #
* <Object>
An object containing previously resolved symbol addresses as bigint values.
library.close() #
Closes the library handle. DynamicLibrary implements the explicit resource management protocol, so a
library instance can be managed with the using declaration. Leaving the
enclosing scope invokes library.close() automatically. import { DynamicLibrary , suffix } from 'node:ffi' ;
{
using lib = new DynamicLibrary ( `./mylib. ${ suffix } ` ) ;
// Use `lib` here; `lib.close()` is called when the block exits.
}
mjs copy
Calling library.close() (or disposing the library) more than once is a no-op. After a library has been closed:
* Resolved function wrappers become invalid.
* Further symbol and function resolution throws.
* Registered callbacks are invalidated.
Closing a library does not make previously exported callback pointers safe to
reuse. Node.js does not track or revoke callback pointers that have already
been handed to native code. If native code still holds a callback pointer after library.close() or after
library.unregisterCallback(pointer) , invoking that pointer has undefined
behavior, is not allowed, and is dangerous: it can crash the process, produce
incorrect output, or corrupt memory. Native code must stop using callback
addresses before the library is closed or before the callback is unregistered. Calling library.close() from one of the library's active callbacks is
unsupported and dangerous. The callback must return before the library is
closed.
library[Symbol.dispose]() #
Added in: v26.1.0
Calls library.close() . This allows DynamicLibrary instances to be used with
the using declaration for automatic cleanup when the enclosing scope
exits. It is a no-op on a library that has already been closed.
library.getFunction(name, signature) #
* name <string>
* signature <Object>
* Returns: <Function>
Resolves a symbol and returns a callable JavaScript wrapper. The returned function has a .pointer property containing the native function
address as a bigint . If the same symbol has already been resolved, requesting it again with a
different signature throws. Requesting it again with the same signature returns
the same function, as does reading it from library.functions . const { DynamicLibrary , suffix } = require ( 'node:ffi' ) ;
const lib = new DynamicLibrary ( `./mylib. ${ suffix } ` ) ;
const add = lib . getFunction ( 'add_i32' , {
arguments : [ 'int32' , 'int32' ] ,
return : 'int32' ,
} ) ;
console . log ( add ( 20 , 22 )) ;
console . log (add . pointer) ;
cjs copy
library.getFunctions([definitions]) #
* definitions <Object>
* Returns: <Object>
When definitions is provided, resolves each named symbol and returns an
object containing callable wrappers. When definitions is omitted, returns wrappers for all functions that have
already been resolved on the library.
library.getSymbol(name) #
* name <string>
* Returns: <bigint>
Resolves a symbol and returns its native address as a bigint .
library.getSymbols() #
* Returns: <Object>
Returns an object containing all previously resolved symbol addresses.
library.registerCallback([signature,] callback) #
* signature <Object>
* callback <Function>
* Returns: <bigint>
Creates a native callback pointer backed by a JavaScript function. When signature is omitted, the callback uses a default void () signature. The return value is the callback pointer address as a bigint . It can be
passed to native functions expecting a callback pointer. const { DynamicLibrary , suffix } = require ( 'node:ffi' ) ;
const lib = new DynamicLibrary ( `./mylib. ${ suffix } ` ) ;
const callback = lib . registerCallback (
{ arguments : [ 'int32' ] , return : 'int32' },
( value ) => value * 2 ,
) ;
cjs copy
Callbacks are subject to the following restrictions:
* They must be invoked on the same system thread where they were created.
* They must not throw exceptions.
* They must not return promises.
* They must return a value compatible with the declared return type.
* They must not call library.close() on their owning library while running.
* They must not unregister themselves while running.
Closing the owning library or unregistering the currently executing callback
from inside the callback is unsupported and dangerous. Doing so may crash the
process, produce incorrect output, or corrupt memory.
library.unregisterCallback(pointer) #
* pointer <bigint>
Releases a callback previously created with library.registerCallback() . Calling library.unregisterCallback(pointer) for a callback that is currently
executing is unsupported and dangerous. The callback must return before it is
unregistered. After library.unregisterCallback(pointer) returns
Links found on this page
- Skip to content [direct]
- Node.js [direct]
- About this documentation [direct]
- Usage and example [direct]
- Assertion testing [direct]
- Asynchronous context tracking [direct]
- Async hooks [direct]
- Buffer [direct]
- C++ addons [direct]
- C/C++ addons with Node-API [direct]
- C++ embedder API [direct]
- Child processes [direct]
- Cluster [direct]
- Command-line options [direct]
- Console [direct]
- Crypto [direct]
- Debugger [direct]
- Deprecated APIs [direct]
- Diagnostics Channel [direct]
- DNS [direct]
- Domain [direct]
- Environment Variables [direct]
- Errors [direct]
- Events [direct]
- File system [direct]
- Globals [direct]
- HTTP [direct]
- HTTP/2 [direct]
- HTTPS [direct]
- Inspector [direct]
- Internationalization [direct]
- Iterable Streams API [direct]
- Modules: CommonJS modules [direct]
- Modules: ECMAScript modules [direct]
- Modules: node:module API [direct]
- Modules: Packages [direct]
- Modules: TypeScript [direct]
- Net [direct]
- OS [direct]
- Path [direct]
- Performance hooks [direct]
- Permissions [direct]
- Process [direct]
- Punycode [direct]
- Query strings [direct]
- Readline [direct]
- REPL [direct]
- Report [direct]
- Single executable applications [direct]
- SQLite [direct]
- Stream [direct]
- String decoder [direct]
- Test runner [direct]
- Timers [direct]
- TLS/SSL [direct]
- Trace events [direct]
- TTY [direct]
- UDP/datagram [direct]
- URL [direct]
- Utilities [direct]
- V8 [direct]
- Virtual File System [direct]
- VM [direct]
- WASI [direct]
- Web Crypto API [direct]
- Web Streams API [direct]
- Worker threads [direct]
- Zlib [direct]
- Code repository and issue tracker [direct]
- Index [direct]
- 26.x [direct]
- View on single page [direct]
- View as JSON [direct]
- Edit on GitHub [direct]
- lib/ffi.js [direct]
- <string> [direct]
- <Object> [direct]
- using [direct]
- <Function> [direct]
- <ArrayBuffer> [direct]