~4Dgifts/toolbox/src/swtools/gencall README Created by Gianni Mariani 5-Dec-1993 DISCLAIMER : This is unsupported code and is intended as an example only. Silicon Graphics may change interface at any time that may render this code example inoperable. This code has also not been tested extensivly so you may find bugs. Silicon Graphics is making no expression of warranty by publishing this code and documentation. In simple words - you use it you take the full responsibility of that action. Any enhancements and bug fixes you may wish to send me can be emailed to gianni@neu.sgi.com however I may not acknowledge it. README : Preface - Machine code generated by high level languages abide by a "calling convention". The "calling convention" is basically a set of assumptions made by the compiler about where function arguments will be found on entry to a function. The MIPS calling convention uses registers to pass the first few arguments and is dependant on what type/float or otherwise is being passed. The "calling convention" also makes assumptions about where values are returned. By far the simplest and most portable way to call a function is to simply write a function call in a high level language. However, for programs that do not know at the time of compilation what arguments are being passed like interpreters or dynamically loaded code, it is difficult to call functions whose specification (return value and arguments) is only known at the time of execution. Description - The following list of files generates a high level programming interface to the MIPS calling convention on SGI machines. This allows a programmer to call any function specified by a call descrption at run time. This can be used by programs that do not know what the call interface is at compile time. List of files: Makefile : makes the test executable genctest README : this. gencall.h : header for these functions gencall.s : assembly level routines gencif.c : high level interface genctest.c : test code The interface is in two layers. Low level (assembler) and high level (call interface). The assebler level implements the minimum that is required in assembler. The assebler level simply creates an argument list area on the stack and then calls a function to copy the related data onto the stack and then calls the function in question, it will then deposit return values into the appropriate return buffer if given. This is done in assembler since it is obvious that it is impossible to manipulate the stack at whim in a high level language. This is basically self contained in gencall.s. Low level example : CallFromGeneralArgs is the function that effects the low level interface. In the case below it calls memcpy to generate the arguments stack made up of the arguments in args. The data is basically copied into the args buffer created by CallFromGeneralArgs and then calling printf. static int args[] = { { (int) "Arg1=%d Arg2=%s Arg3=%d Arg4=%s \n" }, { 5 }, { (int) "This is arg2" }, { 6 }, { (int) "This is arg4" }, }; main() { extern memcpy(); extern printf(); CallFromGeneralArgs( memcpy, args, sizeof( args ), 0, 0, printf ); } High level description : gencif.c implements the high level interface. The GCIF_CALLDESC struct encapsulates all the required information to create an argument list. Typically one would generate an arg list and return value spec from an external interface like an interpreter or a DSO interface file or such. This would be used to generate the args list and the return information. gcif_filldesc would then be called to evaluate the gcif_bytes and gcif_flags values that are used by the assembler level routines. At the time of call, the values of arguments are placed in the call descripion ( gcif_argv, gcif_nargv and gcif_retv ) and together with the address of the function, gcif_call is called which will effect the call to the address given. High level example: The following example shows how to structs as well as giving an example of the high level interface. The structure typed "as" is passed as the first argument as well as being the return value's type. "scd" is initialized to contain the args being passed and the return value. gcif_filldesc is called to initialize the gcif_bytes and gcif_flags fields. This need only be done once after the args and return value are defined. The arguments are initialized and then gcif_call is used to perform the call to "as_SFI". /* structure passing example */ typedef struct { char val[23]; } as; GCIF_ARG sargs[] = { { gcif_struct, /* GCIF_ETYPE gcif_type : 8; */ 4, /* unsigned gcif_align : 8; */ sizeof(as), /* unsigned gcif_size : 16; */ }, { gcif_float, /* GCIF_ETYPE gcif_type : 8; */ 4, /* unsigned gcif_align : 8; */ 4, /* unsigned gcif_size : 16; */ }, { gcif_int, /* GCIF_ETYPE gcif_type : 8; */ 4, /* unsigned gcif_align : 8; */ 4, /* unsigned gcif_size : 16; */ }, }; GCIF_CALLDESC scd[1] = { /* GCIF_ARG * gcif_args; * args */ /* unsigned gcif_nargs; * number of args */ sargs, sizeof( sargs ) / sizeof( * sargs ), gcif_struct, /* GCIF_ETYPE gcif_rettype; */ sizeof( as ), /* unsigned gcif_retsize; */ 0, /* unsigned gcif_bytes; */ 0, /* GCIF_MKFLAGS( gcif_double, FP_PASS( gcif_double, gcif_float ) ), */ /* unsigned gcif_flags; */ /* void ** gcif_argv; * arg value pointers */ /* int gcif_nargv; * number of value pointers */ ARYNI( sargv ), /* void * gcif_retv; * Return value */ }; as as_SFI( as a, float b, int i ) { printf( "as_SFI %s %f %d\n", a.val, b, i ); return a; } example_struct() { double retval; float arg2; int i; as s; as s1; void * sargv[3]; gcif_filldesc( scd ); sargv[ 0 ] = ( void * ) & s; sargv[ 1 ] = ( void * ) & arg2; sargv[ 2 ] = ( void * ) & i; scd->gcif_retv = ( void * ) & s1; scd->gcif_argv = sargv; scd->gcif_nargv = 3; strcpy( s.val, "Hello world" ); arg2 = 122.7; i = 29; gcif_call( as_SFI, scd ); printf( "as_SFI returns %s\n", s1.val ); } MAN PAGE: NAME gencall - generalized call interface C SPECIFICATION #include <gencall.h> int CallFromGeneralArgs( void ( * Prefunc )( void *, void *, unsigned, unsigned * ), void * args, unsigned bytes, unsigned flags, void * retval, GCIF_FUNC_T func ); void gcif_filldesc( GCIF_CALLDESC * args ); int gcif_call( GCIF_FUNC_T * func, GCIF_CALLDESC * args ); void gcif_Prefunc( char * call_args, GCIF_CALLDESC * args, int bytes, unsigned * flags_p ); typedef struct GCIF_CALLDESC { GCIF_ARG * gcif_args; /* args */ unsigned gcif_nargs; /* number of args */ GCIF_ETYPE gcif_rettype; /* return type */ unsigned gcif_retsize; /* return size (for structs)*/ /* Filled in by gcif_filldesc if called. */ unsigned gcif_bytes; /* Bytes required for args*/ unsigned gcif_flags; /* gencall flags */ /* Filled in by user just before being called */ void ** gcif_argv; /* arg value pointers */ int gcif_nargv; /* number of value pointers*/ void * gcif_retv; /* Return value */ } GCIF_CALLDESC; PARAMETERS func address of function to call Prefunc address of function to prepare arguments. The first argument to Prefunc is the pointer to the args to func. The second is the one given to CallFromGeneralArgs, the third is bytes and the fourth is a pointer to flags. args Pointer passed to Prefunc to be used to prepare args before calling func. For gcif_Prefunc, gcif_filldesc and gcif_call, args is a call descriptor. flags Used by CallFromGeneralArgs to determine which registers to load for passing by registers as well as which register contains the return value. All values can be generated by the GCIF_MKFLAGS and FP_PASS macros. Only the first two parameters determine the passing convention needed. The return value is simply a type specifier in the GCIF_ETYPE enum. retval Pointer to the return value. flags_p Pointer to the flags parameter passed to CallFromGeneralArgs. call_args Pointer to arguments frame for function call. DESCRIPTION CallFromGeneralArgs will set up the appropriate argument frame for calling any function. This is usefull in interpreters or if call parameters are determined at run time as may be the case if an application is supplied by a DSO. It does not read any values pointer to by the args parameter, however it passes this to the argument preparation function. CallFromGeneralArgs will also write the return value from the function call to the memory pointed to by retval. Functions that have return values as structs do not use the retval pointer, however they expect the return value's address as the first 'conventional' argument. This must always be a valid pointer. If you use gcif_call, it will take care of struct return values. gcif_filldesc will generate values in the gcif_bytes and gcif_flags fields. These need only be called once after the arguments and return value description is completed. gcif_call will call the function pointed to by func with the arguments as described in the call descriptor pointed to by args. It will correctly call functions returning struct if the return value pointer is nil. It calls CallFromGeneralArgs and uses gcif_Prefunc as a preparation func. gcif_Prefunc is intended to be called from CallFromGeneralArgs as a preparation function. It will copy the arguments described in the GCIF_CALLDESC structure pointed to by args onto a arguments frame pointed to by call_args. DIAGNOSTICS None. BUGS None known.
Source
Documentation
Reference