 |
|
| |
dyncall(3) |
FreeBSD Library Functions Manual |
dyncall(3) |
dyncall —
encapsulation of architecture-, OS- and compiler-specific
function call semantics
#include
<dyncall.h>
DCCallVM *
dcNewCallVM (DCsize
size);
void
dcFree (DCCallVM
* vm);
void
dcMode (DCCallVM
* vm, DCint
mode);
void
dcReset (DCCallVM
* vm);
void
dcArgBool (DCCallVM
* vm, DCbool
arg);
void
dcArgChar (DCCallVM
* vm, DCchar
arg);
void
dcArgShort (DCCallVM
* vm, DCshort
arg);
void
dcArgInt (DCCallVM
* vm, DCint
arg);
void
dcArgLong (DCCallVM
* vm, DClong
arg);
void
dcArgLongLong (DCCallVM
* vm, DClonglong
arg);
void
dcArgFloat (DCCallVM
* vm, DCfloat
arg);
void
dcArgDouble (DCCallVM
* vm, DCdouble
arg);
void
dcArgPointer (DCCallVM
* vm, DCpointer
arg);
void
dcArgAggr (DCCallVM
* vm, const DCaggr *
ag, const void *
value);
DCvoid
dcCallVoid (DCCallVM
* vm, DCpointer
funcptr);
DCbool
dcCallBool (DCCallVM
* vm, DCpointer
funcptr);
DCchar
dcCallChar (DCCallVM
* vm, DCpointer
funcptr);
DCshort
dcCallShort (DCCallVM
* vm, DCpointer
funcptr);
DCint
dcCallInt (DCCallVM
* vm, DCpointer
funcptr);
DClong
dcCallLong (DCCallVM
* vm, DCpointer
funcptr);
DClonglong
dcCallLongLong (DCCallVM
* vm, DCpointer
funcptr);
DCfloat
dcCallFloat (DCCallVM
* vm, DCpointer
funcptr);
DCdouble
dcCallDouble (DCCallVM
* vm, DCpointer
funcptr);
DCpointer
dcCallPointer (DCCallVM
* vm, DCpointer
funcptr);
DCpointer
dcCallAggr (DCCallVM
* vm, DCpointer
funcptr, const DCaggr *
ag, DCpointer
ret);
void
dcBeginCallAggr (DCCallVM
* vm, const DCaggr *
ag);
void
dcArgF (DCCallVM
* vm, const DCsigchar *
signature,
...);
void
dcVArgF (DCCallVM
* vm, const DCsigchar *
signature, va_list
args);
void
dcCallF (DCCallVM
* vm, DCValue *
result, DCpointer
funcptr, const DCsigchar
* signature,
...);
void
dcVCallF (DCCallVM
* vm, DCValue *
result, DCpointer
funcptr, const DCsigchar
* signature, va_list
args);
DCaggr*
dcNewAggr (DCsize
maxFieldCount, DCsize
size);
void
dcAggrField (DCaggr*
ag, DCsigchar type,
DCint offset,
DCsize array_len,
...);
void
dcCloseAggr (DCaggr*
ag);
void
dcFreeAggr (DCaggr*
ag);
The dyncall library encapsulates
architecture-, OS- and compiler-specific function call semantics in a
virtual "bind argument parameters from left to right and then
call" interface allowing programmers to call C functions in a
completely dynamic manner.
In other words, instead of calling a function directly, the
dyncall library provides a mechanism to push the
function parameters manually and to issue the call afterwards.
Since the idea behind this concept is similar to call dispatching
mechanisms of virtual machines, the object that can be dynamically loaded
with arguments, and then used to actually invoke the call, is called CallVM.
It is possible to change the calling convention used by the CallVM at
run-time. Due to the fact that nearly every platform comes with one or more
distinct calling conventions, the dyncall library
project intends to be a portable and open-source approach to the variety of
compiler/toolchain/platform-specific binary interfaces subtleties, and so
on...
dcNewCallVM ()
creates a new CallVM object, where size specifies the
max size of the internal stack that will be allocated and used to bind the
arguments to. Use
dcFree ()
to destroy the CallVM object.
dcMode ()
sets the calling convention to use. See dyncall.h for a list of available
modes. Note that some mode/platform combinations don't make any sense (e.g.
using a PowerPC calling convention on a MIPS platform) and are silently
ignored.
dcReset ()
resets the internal stack of arguments and prepares it for a new call. This
function should be called after setting the initial/main call mode (using
dcMode()), but prior to binding arguments to the CallVM (sometimes dcMode()
calls are needed after pushing some args, e.g.
DC_SIGCHAR_CC_ELLIPSIS_VARARGS, which is used prior to binding varargs of
variadic functions). Use it also when reusing a CallVM, as arguments don't
get flushed automatically after a function call invocation. Note: you should
also call this function after initial creation of the a CallVM object, as
dcNewCallVM doesn't do this, implicitly.
dcArgBool (),
dcArgChar (),
dcArgShort (),
dcArgInt (),
dcArgLong (),
dcArgLongLong (),
dcArgFloat (),
dcArgDouble (),
dcArgPointer ()
and dcArgAggr () are used to bind arguments of the
named types to the CallVM object. Arguments should be bound in
left to
right order regarding the C function prototype.
dcCallVoid (),
dcCallBool (),
dcCallChar (),
dcCallShort (),
dcCallInt (),
dcCallLong (),
dcCallLongLong (),
dcCallFloat (),
dcCallDouble (),
dcCallPointer ()
and dcCallAggr () call the function with the
previously bound arguments and return the named type, where
funcptr is a pointer to the function to call. After
the invocation of the function call, the argument values are still bound to
the CallVM and a second call using the same arguments can be issued. Call
dcReset () (as described above) to clear the internal
argument stack.
The interfaces for passing and/or returning
aggregates (struct, union) by value need to be explained as they are a bit
more complex. Every such argument or return type needs some extra info
describing its layout via a DCaggr structure (except
for non-trivial C++ aggregates, see AGGREGATE DESCRIPTION for more
information, below). Passing such arguments is then done by using
dcArgAggr (),
where ag is a pointer to the description and
value is a pointer to the aggregate in question.
Calling a function that returns an aggregate by value is done via two
functions,
dcBeginCallAggr (),
which handles special cases to facilitate the implementation and
must be
called
before
pushing any arguments, and finally
dcCallAggr ()
where ag is a pointer to the description (for both
calls) and ret points to memory large enough to hold
the to be returned aggregate. dcCallAggr () returns a
pointer to ret.
NOTE:
C++ non-trivial aggregates (check with the std::is_trivial type trait) need
some special handling. First of all, no aggregate description is needed and
NULL must be passed wherever a DCaggr* argument is
needed. Also, as dyncall is oblivious to how to do
any custom/non-trivial construction or copy, and thus cannot do the copy of
the aggregate, passed by-value, itself, the user has to provide such copies,
manually, where needed (e.g. when passing such an aggregate as an argument
by-value, using
dcArgAggr (),
in order to preserver the call's by-value semantics).
dcArgF (),
dcVArgF (),
dcCallF ()
and
dcVCallF ()
can be used to bind arguments in a printf-style call, using a signature
string encoding the argument and return types. The former 2 only bind the
arguments to the vm object (and ignore return types
specified in the signature), whereas the latter two issue a call to the
given function pointer, afterwards. The return value will be stored in
result. The signature string also features calling
convention mode selection. For information about the signature format, refer
to dyncall_signature.h or the dyncall manual.
For passing aggregates using
dc*F ()
functions, pass two varargs for each aggregate, first a pointer to DCaggr,
then a pointer to the aggregate in question. For returning aggregates using
those functions, pass
two final
extra arguments, first a pointer to DCaggr describing the return
value, then a pointer to memory large enough to hold it. An explicit call do
dcBeginCallAggr ()
is not needed in those cases, and a pointer to the to be returned aggregate
is returned via result.
In order to describe an aggregate (except for C++ non-trivial
aggregates, as mentioned above), create a DCaggr object using
dcNewAggr (),
where maxFieldCount is greater or equal to the number
of fields the aggregate has (a nested aggregate or an array is counted as
one field), and size is the size of the aggregate
(e.g. as determined by sizeof()).
dcFreeAggr ()
destroys the DCaggr object.
dcAggrField ()
is used to describe the aggregate, field-by-field (in order), with
type being a DC_SIGCHAR_* (see dyncall_signature.h),
offset being the offset of the field from the
beginning of the aggregate (use C's offsetof(3)), and
array_len being the number of array elements,
iff the
field is an array, otherwise use 1. For nested aggregates (when using
DC_SIGCHAR_AGGREGATE as type), one needs to pass the
pointer to the nested aggregate's DCaggr object as last argument (in
...).
Call
dcCloseAggr ()
after having described all fields of an aggregate.
Note that c99 flexible array members do not count as a field, and
must be omitted, as passing aggregates with a flexible array member by value
in C would also omit it.
Note:
none of the examples below perform any error checking for simplicity of the
example.
Let's start with a simple example, making a call to the function
sqrt(3).
Using the dyncall library, this function would be
called as follows:
double r;
DCCallVM* vm = dcNewCallVM(4096);
dcMode(vm, DC_CALL_C_DEFAULT);
dcReset(vm);
/* call: double sqrt(double x); */
dcArgDouble(vm, 4.2373);
r = dcCallDouble(vm, (DCpointer)&sqrt);
dcFree(vm);
Note that the DCCallVM object can be reused and
shouldn't be created and freed per call, for performance reasons. The
following examples will omit creation and freeing of the
DCCallVM, for simplicity.
In a more complicated example, let's call
printf(3),
which requires a different initial mode, as well as a mode switch for the
varargs part:
int n_written_chars, r;
/* initial callconv mode */
dcMode(vm, DC_CALL_C_ELLIPSIS);
dcReset(vm);
/* int printf(const char * restrict format, ...); */
dcArgPointer(vm, "my printf(%d) %s string%n");
/* switch mode for varargs part */
dcMode(vm, DC_CALL_C_ELLIPSIS_VARARGS);
dcArgInt(vm, 3);
dcArgPointer(vm, "format");
dcArgPointer(vm, &n_written_chars);
r = dcCallInt(vm, (DCpointer)&printf);
Onto an example passing an aggregate by value
(note that this is only available on platforms where macro
DC__Feature_AggrByVal is defined). E.g. passing the
following struct S to f ():
struct S { char x[3]; double y; };
void f(int, struct S);
requires a DCaggr description of the
fields/layout of struct S, and is called as follows:
struct S s = { { 56, -23, 0 }, -6.28 };
DCaggr *a = dcNewAggr(2, sizeof(struct S));
dcAggrField(a, DC_SIGCHAR_CHAR, offsetof(struct S, x), 3);
dcAggrField(a, DC_SIGCHAR_DOUBLE, offsetof(struct S, y), 1);
dcCloseAggr(a);
dcMode(vm, DC_CALL_C_DEFAULT);
dcArgInt(vm, 999);
dcArgAggr(vm, a, &s);
dcCallVoid(vm, (DCpointer)&f);
dcFreeAggr(a);
Let's look at an example returning by value the
above struct S from function:
Omitting creation of the DCaggr
*a description, for simplicity:
struct S s;
dcMode(vm, DC_CALL_C_DEFAULT);
/* needed when returning aggrs by value, *before* pushing args */
dcBeginCallAggr(vm, a);
dcArgInt(vm, 9);
dcArgShort(vm, 7);
dcCallAggr(vm, (DCpointer)&g, a, &s);
In our next example, let's look at calling a simple C++ method,
with the method declaration being:
virtual void Klass::Method(float, int);
To keep the example simple, let's assume we have a pointer to this
virtual method in var mptr (e.g. grabbed from the
instance's vtable), and a pointer to the instance in var
thisptr:
/* thiscall calling convention */
dcMode(vm, DC_CALL_C_DEFAULT_THIS);
dcReset(vm);
/* C++ methods use this-ptr as first/hidden argument */
dcArgPointer(vm, thisptr);
dcArgFloat(vm, 2.3f);
dcArgInt(vm, -19);
dcCallVoid(vm, (DCpointer)mptr);
Extending the last example to a vararg method would need some more
dcMode(3)
calls. E.g.:
virtual void Klass::Method(float, int, ...);
would be called as follows:
/* thiscall calling convention (to pass this-ptr) */
dcMode(vm, DC_CALL_C_DEFAULT_THIS);
dcReset(vm);
/* C++ methods use this-ptr as first/hidden argument */
dcArgPointer(vm, thisptr);
/* fixed part of arguments */
dcMode(vm, DC_CALL_C_ELLIPSIS);
dcArgFloat(vm, 2.3f);
dcArgInt(vm, -19);
/* variable part of arguments */
dcMode(vm, DC_CALL_C_ELLIPSIS_VARARGS);
dcArgInt(vm, 7);
dcArgDouble(vm, 7.99);
dcCallVoid(vm, (DCpointer)mptr);
The dyncall library needs at least a c99 compiler with additional
support for anonymous structs/unions (which were introduced officially in
c11). Given that those are generally supported by pretty much all major c99
conforming compilers (as default extension), it should build fine with a c99
toolchain. Strictly speaking, dyncall conforms to c11, though.
Daniel Adler
⟨dadler@uni-goettingen.de⟩
Tassilo Philipp
⟨tphilipp@potion-studios.com⟩
Visit the GSP FreeBSD Man Page Interface. Output converted with ManDoc.
|