You've already forked linux-packaging-mono
							
							
		
			
				
	
	
		
			136 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			136 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| Author: Dietmar Maurer (dietmar@ximian.com)
 | |
| (C) 2001 Ximian, Inc.
 | |
| 
 | |
| 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"]
 | |
| 	public static extern int puts (string name);
 | |
| 
 | |
| If you then call "puts(...)" it invokes the native "puts" functions in
 | |
| "cygwin1.dll". It is also possible to specify several marshalling attributes
 | |
| for the arguments, for example you can specify that they puts() function expect
 | |
| ts the string in Ansi encoding by setting the CharSet attribute field:
 | |
| 
 | |
| 	[DllImport("cygwin1.dll", EntryPoint="puts", CharSet=CharSet.Ansi)]
 | |
| 	public static extern int puts (string name);
 | |
| 
 | |
| 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.
 | |
| 
 | |
| If you write your own runtime environment you can add internal calls with
 | |
| mono_add_internal_call(). 
 | |
| 
 | |
| 
 | |
| 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.
 | |
| 
 | |
| 4.) When/how does the runtime call unmanaged internal calls
 | |
| 
 | |
| We don't need to convert any 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? 
 | |
| 
 | |
| 
 | |
| 5.) 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. 
 |