1 Author: Dietmar Maurer (dietmar@ximian.com)
4 More about PInvoke and Internal calls
5 =====================================
9 PInvoke stands for Platform Invoke. It is possible to call functions contained
10 in native shared libraries, for example you can declare:
12 [DllImport("cygwin1.dll", EntryPoint="puts"]
13 public static extern int puts (string name);
15 If you then call "puts(...)" it invokes the native "puts" functions in
16 "cygwin1.dll". It is also possible to specify several marshalling attributes
17 for the arguments, for example you can specify that they puts() function expect
18 ts the string in Ansi encoding by setting the CharSet attribute field:
20 [DllImport("cygwin1.dll", EntryPoint="puts", CharSet=CharSet.Ansi)]
21 public static extern int puts (string name);
23 2.) What are internal calls
25 Some class library functions are implemented in C, because it is either not
26 possible to implement them in C# or because of performance gains. Internal
27 functions are contained in the mono executable itself. Here is an example form
28 our array implementation:
30 [MethodImplAttribute(MethodImplOptions.InternalCall)]
31 public extern int GetRank ();
33 If you call this GetRank() function it invokes
34 ves_icall_System_Array_GetRank() inside the mono runtime.
36 If you write your own runtime environment you can add internal calls with
37 mono_add_internal_call().
40 2.) Runtime considerations
42 Invoking native (unmanaged) code has several implications:
44 - We need to handle exceptions inside unmanaged code. The JIT simply saves some
45 informations at each transition from managed to unmanaged code (in a linked
46 list), called Last Managed Frame (LMF). If an exception occurs the runtime
47 first looks if the exception was inside managed code. If not there must be a
48 LMF entry which contains all necessary information to unwind the stack.
50 Creation of those LMF structure clearly involves some overhead, so calling
51 into unmanaged code is not as cheap as it looks like at first glance. Maybe
52 we can introduce a special attribute to avoid the creation of LMF on internal
53 call methods that cant raise exceptions.
55 - PInvoke has the possibility to convert argument types. For example Strings
56 are marshalled as Char*. So each String argument is translated into a
57 char*. The encoding is specified in the CharSet of the DllImport attribute.
60 3.) When/how does the runtime call unmanaged PInvoke code
62 - LDFTN, CALLI, Delegate::Invoke, Delegate::BeginInvoke: We must generate
63 wrapper code when we load the function with LDFTN, so that all arguments are
64 marshalled in the right format. We also need to save/restore the LMF.
66 - MethodBase::Invoke (runtime invoke): We need to marshal all arguments in
67 they right format and save/restore the LMF
69 - CALL: We need to marshal all arguments in they right format and save/restore
72 The easiest way to implement this is to always create a wrapper function for
73 PInvoke calls, which takes care of argument marshalling and LMF save/restore.
75 4.) When/how does the runtime call unmanaged internal calls
77 We don't need to convert any arguments, so we need only take care of the LMF
80 - LDFTN, CALLI, Delegate::Invoke, Delegate::BeginInvoke: We must generate
81 wrapper code when we load the function with LDFTN which saves/restores the
84 - MethodBase::Invoke (runtime invoke): We need to save/restore the LMF.
86 - CALL: We need to save/restore the LMF.
88 - CALLVIRT (through the vtable): We must generate wrapper code to save/restore
91 Please notice that we can call internal function with CALLVIRT, i.e. we can
92 call those function through a VTable. But we cant know in advance if a vtable
93 slot contains an internal call or managed code. So again it is best to generate
94 a wrapper functions for internal calls in order to save/restore the LMF.
96 Unfortunately we need to push all arguments 2 times, because we have to save
97 the LMF, and the LMF is currently allocated on the stack. So the stack looks
108 AFAIK this is the way ORP works. Another way is to allocate the LMF not on the
109 stack, but then we have additional overhead to allocate/free LMF structures
110 (and another call to arch_get_lmf_addr).
112 Maybe it is possible to avoid this addiotional copy for internal calls by
113 including the LMF in the C function signature. Lets say we hav a puts()
114 function which is a internal call:
116 ves_icall_puts (MonoString *string);
118 If we simply modify that to include the LMF we can avoid to copy all arguments:
120 ves_icall_puts (MonoLMF lmf, MonoString *string);
122 But this depends somehow on the calling conventions, and I don't know if that
123 works on all plattforms?
126 5.) What is stored in the LMF
128 - all caller saved registers (since we can trust unmanaged code)
129 - the instruction pointer of the last managed instruction
130 - a MonoMethod pointer for the unmanaged function
131 - the address of the thread local lfm_addr pointer (to avoid another call to
132 arch_get_lmf_addr when restoring LMF)
134 The LMF is allocated on the stack, so we also know the stack position for