Mercurial > pub > dyncall > dyncall
view dyncall/dyncall.3 @ 634:e3441521ad6b
"
author | Tassilo Philipp |
---|---|
date | Sun, 20 Nov 2022 19:46:01 +0100 |
parents | 1d4f0f516483 |
children |
line wrap: on
line source
.\" Copyright (c) 2007-2022 Daniel Adler <dadler AT uni-goettingen DOT de>, .\" Tassilo Philipp <tphilipp AT potion-studios DOT com> .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .Dd $Mdocdate$ .Dt dyncall 3 .Sh NAME .Nm dyncall .Nd encapsulation of architecture-, OS- and compiler-specific function call semantics .Sh SYNOPSIS .In dyncall.h .Ft DCCallVM * .Fn dcNewCallVM "DCsize size" .Ft void .Fn dcFree "DCCallVM * vm" .Ft void .Fn dcMode "DCCallVM * vm" "DCint mode" .Ft void .Fn dcReset "DCCallVM * vm" .Ft void .Fn dcArgBool "DCCallVM * vm" "DCbool arg" .Ft void .Fn dcArgChar "DCCallVM * vm" "DCchar arg" .Ft void .Fn dcArgShort "DCCallVM * vm" "DCshort arg" .Ft void .Fn dcArgInt "DCCallVM * vm" "DCint arg" .Ft void .Fn dcArgLong "DCCallVM * vm" "DClong arg" .Ft void .Fn dcArgLongLong "DCCallVM * vm" "DClonglong arg" .Ft void .Fn dcArgFloat "DCCallVM * vm" "DCfloat arg" .Ft void .Fn dcArgDouble "DCCallVM * vm" "DCdouble arg" .Ft void .Fn dcArgPointer "DCCallVM * vm" "DCpointer arg" .Ft void .Fn dcArgAggr "DCCallVM * vm" "const DCaggr * ag" "const void * value" .Ft DCvoid .Fn dcCallVoid "DCCallVM * vm" "DCpointer funcptr" .Ft DCbool .Fn dcCallBool "DCCallVM * vm" "DCpointer funcptr" .Ft DCchar .Fn dcCallChar "DCCallVM * vm" "DCpointer funcptr" .Ft DCshort .Fn dcCallShort "DCCallVM * vm" "DCpointer funcptr" .Ft DCint .Fn dcCallInt "DCCallVM * vm" "DCpointer funcptr" .Ft DClong .Fn dcCallLong "DCCallVM * vm" "DCpointer funcptr" .Ft DClonglong .Fn dcCallLongLong "DCCallVM * vm" "DCpointer funcptr" .Ft DCfloat .Fn dcCallFloat "DCCallVM * vm" "DCpointer funcptr" .Ft DCdouble .Fn dcCallDouble "DCCallVM * vm" "DCpointer funcptr" .Ft DCpointer .Fn dcCallPointer "DCCallVM * vm" "DCpointer funcptr" .Ft DCpointer .Fn dcCallAggr "DCCallVM * vm" "DCpointer funcptr" "const DCaggr * ag" "DCpointer ret" .Ft void .Fn dcBeginCallAggr "DCCallVM * vm" "const DCaggr * ag" .Ft void .Fn dcArgF "DCCallVM * vm" "const DCsigchar * signature" "..." .Ft void .Fn dcVArgF "DCCallVM * vm" "const DCsigchar * signature" "va_list args" .Ft void .Fn dcCallF "DCCallVM * vm" "DCValue * result" "DCpointer funcptr" "const DCsigchar * signature" "..." .Ft void .Fn dcVCallF "DCCallVM * vm" "DCValue * result" "DCpointer funcptr" "const DCsigchar * signature" "va_list args" .Ft DCaggr* .Fn dcNewAggr "DCsize maxFieldCount" "DCsize size" .Ft void .Fn dcAggrField "DCaggr* ag" "DCsigchar type" "DCint offset" "DCsize array_len" "..." .Ft void .Fn dcCloseAggr "DCaggr* ag" .Ft void .Fn dcFreeAggr "DCaggr* ag" .Sh DESCRIPTION The .Nm 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. .Pp In other words, instead of calling a function directly, the .Nm library provides a mechanism to push the function parameters manually and to issue the call afterwards. .Pp 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 .Nm 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... .Pp .Fn dcNewCallVM creates a new CallVM object, where .Ar size specifies the max size of the internal stack that will be allocated and used to bind the arguments to. Use .Fn dcFree to destroy the CallVM object. .Pp .Fn 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. .Pp .Fn 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. .Pp .Fn dcArgBool , .Fn dcArgChar , .Fn dcArgShort , .Fn dcArgInt , .Fn dcArgLong , .Fn dcArgLongLong , .Fn dcArgFloat , .Fn dcArgDouble , .Fn dcArgPointer and .Fn dcArgAggr are used to bind arguments of the named types to the CallVM object. Arguments should be bound in .Em "left to right" order regarding the C function prototype. .Pp .Fn dcCallVoid , .Fn dcCallBool , .Fn dcCallChar , .Fn dcCallShort , .Fn dcCallInt , .Fn dcCallLong , .Fn dcCallLongLong , .Fn dcCallFloat , .Fn dcCallDouble , .Fn dcCallPointer and .Fn dcCallAggr call the function with the previously bound arguments and return the named type, where .Ar 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 .Fn dcReset (as described above) to clear the internal argument stack. .Pp 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 .Ft DCaggr structure (except for non-trivial C++ aggregates, see AGGREGATE DESCRIPTION for more information, below). Passing such arguments is then done by using .Fn dcArgAggr , where .Ar ag is a pointer to the description and .Ar value is a pointer to the aggregate in question. Calling a function that returns an aggregate by value is done via two functions, .Fn dcBeginCallAggr , which handles special cases to facilitate the implementation and .Em must be called .Em before pushing any arguments, and finally .Fn dcCallAggr where .Ar ag is a pointer to the description (for both calls) and .Ar ret points to memory large enough to hold the to be returned aggregate. .Fn dcCallAggr returns a pointer to .Ar ret . .Pp .Em 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 .Ft DCaggr* argument is needed. Also, as .Nm 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 .Fn dcArgAggr , in order to preserver the call's by-value semantics). .Pp .Fn dcArgF , .Fn dcVArgF , .Fn dcCallF and .Fn 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 .Ar 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 .Ar result . The signature string also features calling convention mode selection. For information about the signature format, refer to dyncall_signature.h or the .Nm manual. .Pp For passing aggregates using .Fn 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 .Em "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 .Fn dcBeginCallAggr is not needed in those cases, and a pointer to the to be returned aggregate is returned via .Ar result . .Sh AGGREGATE DESCRIPTION In order to describe an aggregate (except for C++ non-trivial aggregates, as mentioned above), create a DCaggr object using .Fn dcNewAggr , where .Ar 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 .Ar size is the size of the aggregate (e.g. as determined by sizeof()). .Pp .Fn dcFreeAggr destroys the DCaggr object. .Pp .Fn dcAggrField is used to describe the aggregate, field-by-field (in order), with .Ar type being a DC_SIGCHAR_* (see dyncall_signature.h), .Ar offset being the offset of the field from the beginning of the aggregate (use C's offsetof(3)), and .Ar array_len being the number of array elements, .Em iff the field is an array, otherwise use 1. For nested aggregates (when using DC_SIGCHAR_AGGREGATE as .Ft type ) , one needs to pass the pointer to the nested aggregate's DCaggr object as last argument (in .Ar ... ) . .Pp Call .Fn dcCloseAggr after having described all fields of an aggregate. .Pp 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. .Sh EXAMPLES .Em Note : none of the examples below perform any error checking for simplicity of the example. .Pp Let's start with a simple example, making a call to the function .Xr sqrt 3 . Using the .Nm library, this function would be called as follows: .Bd -literal -offset indent 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); .Ed .Pp Note that the .Sy 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 .Sy DCCallVM , for simplicity. .Pp In a more complicated example, let's call .Xr printf 3 , which requires a different initial mode, as well as a mode switch for the varargs part: .Bd -literal -offset indent 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); .Ed .Pp .Ss C/trivial aggregates by-value Onto an example passing an aggregate .Em "by value" (note that this is only available on platforms where macro .Dv DC__Feature_AggrByVal is defined). E.g. passing the following .Sy struct S to .Fn f : .Bd -literal -offset indent struct S { char x[3]; double y; }; void f(int, struct S); .Ed .Pp requires a .Sy DCaggr description of the fields/layout of .Sy struct S , and is called as follows: .Bd -literal -offset indent 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); .Ed .Pp Let's look at an example returning .Em "by value" the above .Sy struct S from function: .Bd -literal -offset indent struct S g(int, short); .Ed .Pp Omitting creation of the .Sy DCaggr .Ar *a description, for simplicity: .Bd -literal -offset indent 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); .Ed .Ss C++ In our next example, let's look at calling a simple C++ method, with the method declaration being: .Bd -literal -offset indent virtual void Klass::Method(float, int); .Ed .Pp To keep the example simple, let's assume we have a pointer to this virtual method in var .Ar mptr (e.g. grabbed from the instance's vtable), and a pointer to the instance in var .Ar thisptr : .Bd -literal -offset indent /* 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); .Ed .Pp Extending the last example to a vararg method would need some more .Xr dcMode 3 calls. E.g.: .Bd -literal -offset indent virtual void Klass::Method(float, int, ...); .Ed .Pp would be called as follows: .Bd -literal -offset indent /* 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); .Ed .Pp .Sh CONFORMING TO 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. .Ed .Sh SEE ALSO .Xr dyncallback 3 , .Xr dynload 3 and the .Nm manual (available in HTML and PDF format) for more information. .Sh AUTHORS .An "Daniel Adler" Aq dadler@uni-goettingen.de .An "Tassilo Philipp" Aq tphilipp@potion-studios.com