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