Awesome
C-bind: Dynamic function binding in C !!!
Table of Contents
Requirements
Currently C-bind
requires pthread
to be installed, and requires x86_64
. C-bind
has only been tested on Ubuntu 18.04
, compiled with gcc 7.3
.
Usage
General
Includes
To link the C-bind
library simply include the header file bind.h
.
Note
Passing extra arguments to a bound func will result in them being ignored.
SystemV vs Non-SystemV
The most common calling convention of x86_64
/ amd64
is called SystemV. Unless otherwise specified, most major compilers should compile your code to meet this standard. This library provides functions for binding functions that follow the SystemV calling convention; functions that are not compliant have an API for them is provided for them as well. The default API of this library assumes SystemV functions are being bound.
Important notes
- This library uses a signal internally. By default this is
SIGUSR2
, however the user may change this whenever. This signal handler is install when-needed and restored when not, so for a single threaded application this is perfectly safe. However, in a multi-threaded enviornment it is important to set this signal to some (valid) unused signal! This can be done with thebind_set_signal_number
function.
Thread Safe
Yes, as long as this library is the only thing that invokes the signal set by bind_set_signal_number
.
Restrictions
- This library may not work with variadic functions
- This library may fail for SystemV if registers other than
rdi
,rsi,
rdx
,rcx
,r8
, andr9
are used to pass arguments. However this is exceedingly rare. - This library will only compile for
x86_64
/amd64
SystemV
Function Signature
SystemV functions to be bound must return an object of type ret_t
(which should be 8 bytes) or smaller; void
is also valid. Do not attempt to return a large struct as it may fail! As for the arguments of the function, there are no restrictions except that the function may not be variadic! For more info look in the bind.h
file.
Full binding
To fully bind a functon, invoke
bound_func = full_bind( my_func, num_args, arg1, arg2, arg3 );
Here num_args
is the number of arguments to pass to be passed to my_func
. If more than num_args
arguments are passed they will be ignored.
Full binding example
int sum( int a, int b ) { return a + b; }
FullBound bound_func = full_bind( sum, 2, /* Arguments begin */ 1, 2 );
printf( "sum(1,2) = %d", (int) bound_func() );
The output of this code is: sum(1,2) = 3
.
Partial binding
To partially bind a function, invoke
bound_func = partial_bind( my_func, num_args, num_args_to_bind, arg1, arg2 );
Here num_args_to_bind
is the number of arguments currently being bound!
It is worth noting that fully binding a function via a partial bind is supported.
Partial binding Example
int sum3(int a, int b, int c) { return a + b + c; }
PartBound bound_func = partial_bind( sum3, 3, 2, /* Arguments begin */ 100, 200 );
printf( "Total sum = %d", (int) bound_func(300) );
The output of this code is: Total sum = 600
Non-SystemV
Signature
Non-SystemV functions to be bound must have a unique signature, however when calling them they may be called as standard functions. To bind a function, it must have the following signature:
ret_t my_func( arg_t * args );
A ret_t
is simply a void *
. A non-void *
may be returned via casting so long as it is of equal or lesser size. You can think of args
as an array of arguments! The function being bound may not be variadic, consequently, my_func
must have a defined maximum number of 'arguments'. That is, my_func
must expect that no more num_args
number of elements to be passed. For more info look in the bind.h
file. Parsing the args
array is the job of my_func
.
Full binding
To fully bind a function, invoke
bound_func = full_bind( my_func, num_args, arg1, arg2, arg3 );
Here num_args
is the number of elements in the args
array that my_func
expects to be passed. If more arguments than num_args
arguments are passed in they will be ignored.
Full binding Example
ret_t sum( arg_t * args ) { return args[0] + args[1]; }
FullBound bound_func = full_systemv_bind( sum, 2, /* Arguments begin */ 1, 2 );
printf( "sum(1,2) = %d", (int) bound_func() );
The output of this code is: sum(1,2) = 3
.
Partial binding
To partially bind a function, invoke
bound_func = partial_bind( my_func, num_args, num_args_to_bind, arg1, arg2 );
Here num_args_to_bind
is the number of arguments currently being bound!
It is worth noting that fully binding a function via a partial bind is supported.
Partial binding Example
ret_t sum3( arg_t * args ) { return args[0] + args[1] + args[2]; }
PartBound bound_func = partial_systemv_bind( sum3, 3, 2, /* Arguments begin */ 100, 200 );
printf( "Total sum = %d", (int) bound_func(300) );
The output of this code is: Total sum = 600
Examples
To test the examples first compile the code
git clone https://github.com/zwimer/C-bind && \
mkdir C-bind/build && cd C-bind/build && \
cmake ../examples && make
After that, run your desired test. Either ./test.out
(for SystemV tests) or ./test-non-systemv.out
.
Compilation
On gcc
version7.3
, this library is able to compile even with the -O3
flag. If you experience issues however, try compiling at a lower optimization level. See the CMakeLists.txt
file in the examples
directory for an example.
Docker
A Dockerfile
is provided with C-bind
and example cases installed and build. The image is hosted here on docker.com. To pull the image just execute:
docker pull zwimer/c-bind
To run the container simply execute:
docker run --rm -it zwimer/c-bind
If you would like to build the container yourself execute:
git clone https://github.com/zwimer/C-bind && \
cd C-bind && \
docker build -t zwimer/c-bind .
CI
Continuous Integration is provided by Travis CI. To view the CI setup, click here.
Documentation
Documentation is stored in on the gh-pages
branch and hosted here on zwimer.com. Documentation is generated via doxygen. To manually generate it install doxygen (from apt-get
if you have it) then
cd C-bind && doxygen
Future Plans
- Right now hidden in the internals of how the binding mechanism, the
get_stub
function maps an entire page of memory per stub generated. Realistically it should only require just a few bytes. This can be done by placing multiple stub functions on the same page.