Call C functions in shared libraries using libffi for Lua.
This module allows you to load shared libraries (.so, .dylib, .dll) into your Lua scripts, look up functions within those libraries, and call them as if they were native Lua functions. It handles the conversion of data types between Lua and C.
You need to have libffi installed on your system.
# On Debian/Ubuntu
sudo apt-get install libffi-dev
# On RedHat/CentOS
sudo yum install libffi-devel
# On macOS (using Homebrew)
brew install libffiThen, you can install lua-dlopen using LuaRocks:
luarocks install dlopenlocal dlopen = require('dlopen')
-- load libc
local libc, err = dlopen('libc.so.6')
if not libc then
error(err)
end
-- define function signature of `puts`
-- int puts(const char *s);
local ok, err = libc:dlsym('int', 'puts', 'char*')
if not ok then
error(err)
end
-- call `puts`
libc:puts('hello world')
-- define function signature of `strlen`
-- size_t strlen(const char *s);
ok, err = libc:dlsym('size_t', 'strlen', 'char*')
if not ok then
error(err)
end
-- call `strlen`
local len = libc:strlen('hello world')
print(len)
-- close library
ok, err = libc:dlclose()
if not ok then
error(err)
endLoads a shared library from the given path.
Parameters:
path:string: The path to the shared library file.
Returns:
dso:dlopen: An instance of thedlopen, ornilon failure.err:string: An error message if loading fails.
Example:
local lib, err = dlopen('libc.so.6')
if not lib then
error(err)
endCloses the shared library and unloads it from memory. All defined symbols from this library will no longer be available.
Returns:
ok:boolean:trueon success,falseon failure.err:string: An error message if closing fails.
Example:
local ok, err = lib:dlclose()
if not ok then
error(err)
endLoads a symbol (function) from the shared library and defines its signature.
Parameters:
return_type:string: The return type of the C function. See Supported Data Types.function_name:string: The name of the function to look up....:string: A variable number of strings representing the argument types of the C function. See Supported Data Types.
Returns:
ok:boolean:trueon success,falseon failure.err:string: An error message if the symbol lookup or definition fails.
Example:
-- define strlen: size_t strlen(const char *s);
local ok, err = lib:dlsym('size_t', 'strlen', 'char*')
if not ok then
error(err)
endCalls a previously defined C function.
Parameters:
...: The arguments to pass to the C function. These must match the types defined withdlsym.
Returns:
retval:any: The return value from the C function, converted to a corresponding Lua type.
Example:
local len = lib:strlen('hello')
print(len) -- 5The following string identifiers can be used for return_type and arg_types in dlopen:dlsym.
Note: For pointer types, nil is converted to NULL when passed as arguments, and NULL return values are converted to nil.
| Type String | C Type | Lua Type |
|---|---|---|
void |
void |
N/A (not allowed) |
void* |
void* |
nil, lightuserdata, userdata |
char* |
char* |
nil or string |
char |
char |
number (integer) |
signed char |
signed char |
number (integer) |
unsigned char |
unsigned char |
number (integer) |
short |
short |
number (integer) |
unsigned short |
unsigned short |
number (integer) |
int |
int |
number (integer) |
unsigned int |
unsigned int |
number (integer) |
int8 |
int8_t |
number (integer) |
uint8 |
uint8_t |
number (integer) |
int16 |
int16_t |
number (integer) |
uint16 |
uint16_t |
number (integer) |
int32 |
int32_t |
number (integer) |
uint32 |
uint32_t |
number (integer) |
int64 |
int64_t |
number (integer) |
uint64 |
uint64_t |
number (integer) |
long |
long |
number (integer) |
unsigned long |
unsigned long |
number (integer) |
long long |
long long |
number (integer) |
unsigned long long |
unsigned long long |
number (integer) |
float |
float |
number |
double |
double |
number |
size_t |
size_t |
number (integer) |
ssize_t |
ssize_t |
number (integer) |
- Currently not supported; arrays require special handling in libffi
- Design:
dlopen:dlsym(return_type, function_name, type[], length)or similar syntax
- Functions like
strtolrequire pointer-to-pointer arguments (char **endptr) - Current limitation: using
void*forchar**causes type mismatch and undefined behavior - Design: Add
void**andchar**type strings to properly handle output parameters - Example:
lib:dlsym('long', 'strtol', 'char*', 'char**', 'int')
The following features are being considered but not yet committed to implementation:
- Design:
dlopen:defstruct(name, fields) - Field syntax:
'field_name@type'(e.g.,'x@int','str@char*') - Extended syntax:
- Arrays:
'arr@int#10'→int arr[10]; - Bit-fields:
'flag@uint:1'→unsigned int flag:1; - Function pointers:
'callback@int(int,int)'→int (*callback)(int, int); - Multi-level pointers:
'pptr@char**'→char** pptr;(requires type system extension) - Const:
'cstr@const char*'→const char* cstr;(requires type system extension)
- Arrays:
- Predefined function pointer types:
-- Define function pointer type dlopen:defcallback(return_type, type_name, ...arg_types) -- Examples dlopen:defcallback('int', 'IntCallback', 'int', 'int') dlopen:defcallback('void*', 'MallocFunc', 'size_t') dlopen:defcallback('int', 'CompareFunc', 'const void*', 'const void*') -- Use in struct definition dlopen:defstruct('MyData', { 'x@int', 'callback@IntCallback', -- use predefined type 'malloc@MallocFunc', 'compare@CompareFunc' })
typedef int (*IntCallback)(int, int); typedef void* (*MallocFunc)(size_t); typedef int (*CompareFunc)(const void*, const void*); struct MyData { int x; IntCallback callback; MallocFunc malloc; CompareFunc compare; };
- Anonymous structs/unions (supports nesting):
dlopen:defstruct('MyData', { 'x@int', 'struct', -- anonymous struct without field name { 'str@char*', 'len@size_t' }, 'union@value', -- anonymous union with field name { 'i@int', 'f@float' }, 'field@PredefType' -- use predefined struct type })
struct MyData { int x; struct { char* str; size_t len; }; union { int i; float f; } value; PredefType field; };
-- Nested anonymous structs/unions are supported dlopen:defstruct('NestedData', { 'x@int', 'struct', { 'str@char*', 'nested@struct', -- nested anonymous struct { 'foo@int', 'bar@int' } } })
struct NestedData { int x; struct { char* str; struct { int foo; int bar; } nested; }; };
- Data mapping: Lua table ↔ C struct
- Complexity: High - requires ABI alignment handling and type conversion
- Allow passing Lua functions as C callbacks
- Complexity: High - requires managing callback lifetime and cleanup
- libffi requires special handling for
... - Complexity: Medium - could use separate API like
dlopen:dlsym_variadic()
This project is licensed under the MIT License - see the LICENSE file for details.