Single-header dynamic function invocation in C
Simply clone this repo or copy call.h
and include it.
This can be achieved with a make rule like this:
call.h:
wget https://raw.githubusercontent.com/loglob/call.h/main/call.h
The header only works for 64-bit x86_64 Linux/System-V executables.
I've only tested this with GCC and it uses a few GCC extensions (such as inline assembly), so it probably won't work for other compilers.
It also requires at least C11 for the _Generic
keyword.
After including call.h
, use this declaration
argls s = {};
to initialize a new, empty argument list.
After initializing an argument list, arguments can be appended to it by calling various functions and macros, beginning with the leftmost argument.
The simplest way of adding an argument is argls_add()
which uses a generic to attempt to find the correct argument class automatically for most primitive types.
Note that pointers should be cast to void*
to use this macro.
If the argument class of an argument is known, one of argls_add_memory()
, argls_add_memv()
, argls_add_integer()
or argls_add_sse()
can be used.
Note that argls_add_memory()
takes the address of an object and argls_add_memv()
assumes that the object is already in the memory class and copies data from its own argument list.
All of these macros return a boolean, with false indicated error due to malloc failure when expansing the stack.
All of these macros are only intended for primitive types and won't handle most structs or unions properly.
Unfortunately, there is no way to automatically detect the argument class(es) for those types, so in order to use them in a dynamic invocation, you will have to look into the Linux/System-V ABI documentation yourself and determine the correct argument classes and order.
After settings up an argument list, the call()
function is used to call a function address.
The return value of the function should be considered lost, although integer or sse class arguments have a good chance to still be in their respective registers, but this should absolutely not be relied upon.
After invokation, the argument list is not mutated, so it can be used in another call, or be further modified.
It is destroyed by using argls_end()
.
By declaring DYNAMIC_REGS
, the register arrays of the argument list struct are stored in dynamic memory instead of on the stack (the default behaviour).
This reduces the size of an argument list from 192 bytes down to 32.