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.  |