diff options
author | Dietmar Maurer <dietmar@mono-cvs.ximian.com> | 2002-03-14 12:52:53 +0300 |
---|---|---|
committer | Dietmar Maurer <dietmar@mono-cvs.ximian.com> | 2002-03-14 12:52:53 +0300 |
commit | 7a8a86c3b9d6f74c61f3e6ec2435e6fc78eac5ab (patch) | |
tree | 45d04f3f239c4b69dbcea816f681f6bef5202e89 /docs/unmanaged-calls | |
parent | d4b18d1e824129bb2b556eaa2ccaa5dc72b1b6f4 (diff) |
2002-03-14 Dietmar Maurer <dietmar@ximian.com>
* emit-x86.c (arch_create_native_wrapper): new code to generate
wrappers for calling native functions.
* icall.c (ves_icall_InternalInvoke): impl.
svn path=/trunk/mono/; revision=3103
Diffstat (limited to 'docs/unmanaged-calls')
-rw-r--r-- | docs/unmanaged-calls | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/docs/unmanaged-calls b/docs/unmanaged-calls new file mode 100644 index 00000000000..944abd7d19e --- /dev/null +++ b/docs/unmanaged-calls @@ -0,0 +1,122 @@ +More about PInvoke and Internal calls +===================================== + +1.) What is PInvoke + +PInvoke stands for Platform Invoke. It is possible to call functions contained +in native shared libraries, for example you can declare: + + [DllImport("cygwin1.dll", EntryPoint="puts", CharSet=CharSet.Ansi)] + public static extern int puts (string name); + +If you then call "puts(...)" it invokes the native "puts" functions in +"cygwin1.dll" + +2.) What are internal calls + +Some class library functions are implemented in C, because it is either not +possible to implement them in C# or because of performance gains. Internal +functions are contained in the mono executable itself. Here is an example form +our array implementation: + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + public extern int GetRank (); + +If you call this GetRank() function it invokes +ves_icall_System_Array_GetRank() inside the mono runtime. + +2.) Runtime considerations + +Invoking native (unmanaged) code has several implications: + +- We need to handle exceptions inside unmanaged code. The JIT simply saves some + informations at each transition from managed to unmanaged code (in a linked + list), called Last Managed Frame (LMF). If an exception occurs the runtime + first looks if the exception was inside managed code. If not there must be a + LMF entry which contains all necessary information to unwind the stack. + + Creation of those LMF structure clearly involves some overhead, so calling + into unmanaged code is not as cheap as it looks like at first glance. Maybe + we can introduce a special attribute to avoid the creation of LMF on internal + call methods that cant raise exceptions. + +- PInvoke has the possibility to convert argument types. For example Strings + are marshalled as Char*. So each String argument is translated into a + char*. The encoding is specified in the CharSet of the DllImport attribute. + + +3.) When/how does the runtime call unmanaged PInvoke code + +- LDFTN, CALLI, Delegate::Invoke, Delegate::BeginInvoke: We must generate + wrapper code when we load the function with LDFTN, so that all arguments are + marshalled in the right format. We also need to save/restore the LMF. + +- MethodBase::Invoke (runtime invoke): We need to marshal all arguments in + they right format and save/restore the LMF + +- CALL: We need to marshal all arguments in they right format and save/restore + the LMF + +The easiest way to implement this is to always create a wrapper function for +PInvoke calls, which takes care of argument marshalling and LMF save/restore. + +3.) When/how does the runtime call unmanaged internal calls + +We don't need to convert and arguments, so we need only take care of the LMF +structure. + +- LDFTN, CALLI, Delegate::Invoke, Delegate::BeginInvoke: We must generate + wrapper code when we load the function with LDFTN which saves/restores the + LMF. + +- MethodBase::Invoke (runtime invoke): We need to save/restore the LMF. + +- CALL: We need to save/restore the LMF. + +- CALLVIRT (through the vtable): We must generate wrapper code to save/restore + the LMF. + +Please notice that we can call internal function with CALLVIRT, i.e. we can +call those function through a VTable. But we cant know in advance if a vtable +slot contains an internal call or managed code. So again it is best to generate +a wrapper functions for internal calls in order to save/restore the LMF. + +Unfortunately we need to push all arguments 2 times, because we have to save +the LMF, and the LMF is currently allocated on the stack. So the stack looks +like: + + -------------------- + | method arguments | + -------------------- + | LMF | + -------------------- + | copied arguments | + -------------------- + +AFAIK this is the way ORP works. Another way is to allocate the LMF not on the +stack, but then we have additional overhead to allocate/free LMF structures +(and another call to arch_get_lmf_addr). + +Maybe it is possible to avoid this addiotional copy for internal calls by +including the LMF in the C function signature. Lets say we hav a puts() +function which is a internal call: + +ves_icall_puts (MonoString *string); + +If we simply modify that to include the LMF we can avoid to copy all arguments: + +ves_icall_puts (MonoLMF lmf, MonoString *string); + +But this depends somehow on the calling conventions, and I don't know if that +works on all plattforms? + +4.) What is stored in the LMF + +- all caller saved registers (since we can trust unmanaged code) +- the instruction pointer of the last managed instruction +- a MonoMethod pointer for the unmanaged function +- the address of the thread local lfm_addr pointer (to avoid another call to + arch_get_lmf_addr when restoring LMF + +The LMF is allocated on the stack, so we also know the stack position for +stack unwinding. |