2019-05-07 08:35:39 +03:00
# if defined ( __arm64 __ )
# include < stdint . h >
# include < stdlib . h >
# include < stdio . h >
# include < string . h >
# include < objc / objc . h >
# include < objc / runtime . h >
# include < objc / message . h >
# include "trampolines-internal.h"
# include "xamarin/runtime.h"
# include "runtime-internal.h"
# include "trampolines-arm64.h"
# include "product.h"
/ *
* https : // developer . apple . com / library / archive / documentation / Xcode / Conceptual / iPhoneOSABIReference / Articles / ARM64FunctionCallingConventions . html
*
* Standard arm64 calling convention :
* Input :
* x0 - x7 ( x8 too in some cases )
* q0 - q7 ( simd / floating point )
* Output :
* same as input
*
* /
# ifdef TRACE
static void
dump_state ( struct XamarinCallState * state , const char * prefix )
{
fprintf ( stderr , "%stype: %llu self: %p SEL: %s sp: 0x%llx x0: 0x%llx x1: 0x%llx x2: 0x%llx x3: 0x%llx x4: 0x%llx x5: 0x%llx x6: 0x%llx x7: 0x%llx x8: 0x%llx -- q0: %Lf q1: %Lf q2: %Lf q3: %Lf q4: %Lf q5: %Lf q6: %Lf q7: %Lf\n" ,
prefix , state -> type , state -> self ( ) , sel_getName ( state -> sel ( ) ) , state -> sp , state -> x0 , state -> x1 , state -> x2 , state -> x3 , state -> x4 , state -> x5 , state -> x6 , state -> x7 , state -> x8 ,
state -> q0 . d , state -> q1 . d , state -> q2 . d , state -> q3 . d , state -> q4 . d , state -> q5 . d , state -> q6 . d , state -> q7 . d ) ;
}
# else
# define dump_state ( . . . )
# endif
2019-12-12 06:17:29 +03:00
static int
2021-02-11 10:18:38 +03:00
param_read _primitive ( struct ParamIterator * it , const char * type_ptr , void * target , size_t total_size , GCHandle * exception_gchandle )
2019-05-07 08:35:39 +03:00
{
// COOP : does not access managed memory : any mode .
char type = * type_ptr ;
LOGZ ( " reading primitive %c. total size: %i nsrn: %i ngrn: %i nsaa: %p\n" , type , ( int ) total_size , it -> nsrn , it -> ngrn , it -> nsaa ) ;
switch ( type ) {
case _C _FLT : {
if ( it -> nsrn < 8 ) {
if ( target ! = NULL ) {
* ( float * ) target = * ( float * ) & it -> q [ it -> nsrn ] ;
LOGZ ( " reading float at q%i into %p: %f\n" , it -> nsrn , target , * ( float * ) target ) ;
}
it -> nsrn + + ;
} else {
if ( target ! = NULL ) {
* ( float * ) target = * ( float * ) it -> nsaa ;
LOGZ ( " reading float at stack %p into %p: %f\n" , it -> nsaa , target , * ( float * ) target ) ;
}
it -> nsaa + = 4 ;
}
return 4 ;
}
case _C _DBL : {
if ( it -> nsrn < 8 ) {
if ( target ! = NULL ) {
* ( double * ) target = * ( double * ) & it -> q [ it -> nsrn ] ;
LOGZ ( " reading double at q%i into %p: %f\n" , it -> nsrn , target , * ( double * ) target ) ;
}
it -> nsrn + + ;
} else {
if ( target ! = NULL ) {
* ( double * ) target = * ( double * ) it -> nsaa ;
LOGZ ( " reading double at stack %p into %p: %f\n" , it -> nsaa , target , * ( double * ) target ) ;
}
it -> nsaa + = 8 ;
}
return 8 ;
}
default : {
size_t size = xamarin_get _primitive _size ( type ) ;
if ( size = = 0 )
return 0 ;
uint8_t * ptr ;
bool read_register = it -> ngrn < 8 ;
if ( read_register ) {
ptr = ( uint8_t * ) & it -> x [ it -> ngrn ] ;
if ( target ! = NULL ) {
LOGZ ( " reading primitive of size %i from x%i into %p: " , ( int ) size , it -> ngrn , target ) ;
}
it -> ngrn + + ;
} else {
ptr = ( uint8_t * ) it -> nsaa ;
if ( target ! = NULL ) {
LOGZ ( " reading primitive of size %i from %p into %p: " , ( int ) size , ptr , target ) ;
}
it -> nsaa + = size ;
}
if ( target = = NULL ) {
LOGZ ( " not reading, since target is NULL.\n" ) ;
2019-12-12 06:17:29 +03:00
return ( int ) size ;
2019-05-07 08:35:39 +03:00
}
switch ( size ) {
case 8 :
* ( uint64_t * ) target = * ( uint64_t * ) ptr ;
LOGZ ( "0x%llx = %llu\n" , * ( uint64_t * ) target , * ( uint64_t * ) target ) ;
break ;
case 4 :
* ( uint32_t * ) target = * ( uint32_t * ) ptr ;
LOGZ ( "0x%x = %u\n" , * ( uint32_t * ) target , * ( uint32_t * ) target ) ;
break ;
case 2 :
* ( uint16_t * ) target = * ( uint16_t * ) ptr ;
LOGZ ( "0x%x = %u\n" , ( int ) * ( uint32_t * ) target , ( int ) * ( uint32_t * ) target ) ;
break ;
case 1 :
* ( uint8_t * ) target = * ( uint8_t * ) ptr ;
LOGZ ( "0x%x = %u = '%c'\n" , ( int ) * ( uint8_t * ) target , ( int ) * ( uint8_t * ) target , ( char ) * ( uint8_t * ) target ) ;
break ;
default :
* exception_gchandle = xamarin_create _mt _exception ( xamarin_strdup _printf ( PRODUCT ": Cannot marshal parameter type %c (size: %i): invalid size.\n" , type , ( int ) size ) ) ;
return 0 ;
}
2019-12-12 06:17:29 +03:00
return ( int ) size ;
2019-05-07 08:35:39 +03:00
}
}
}
static void
2021-02-11 10:18:38 +03:00
param_iter _next ( enum IteratorAction action , void * context , const char * type , size_t size , void * target , GCHandle * exception_gchandle )
2019-05-07 08:35:39 +03:00
{
// COOP : does not access managed memory : any mode .
struct ParamIterator * it = ( struct ParamIterator * ) context ;
if ( action = = IteratorStart ) {
it -> ngrn = 2 ; // we already have two arguments : self + SEL
it -> nsrn = 0 ;
it -> nsaa = ( uint8_t * ) it -> state -> sp ;
it -> x = & it -> state -> x0 ;
it -> q = & it -> state -> q0 ;
LOGZ ( " initialized parameter iterator. next register: %i next fp register: %i next stack pointer: %p\n" , it -> ngrn , it -> nsrn , it -> nsaa ) ;
return ;
} else if ( action = = IteratorEnd ) {
return ;
}
// target must be at least pointer sized , and we need to zero it out first .
if ( target ! = NULL )
* ( uint64_t * ) target = 0 ;
char struct_name [ 5 ] ; // we don ' t care about structs with more than 4 fields .
xamarin_collapse _struct _name ( type , struct_name , sizeof ( struct_name ) , exception_gchandle ) ;
2021-02-11 10:18:38 +03:00
if ( * exception_gchandle ! = INVALID_GCHANDLE )
2019-05-07 08:35:39 +03:00
return ;
if ( size > 16 && strcmp ( struct_name , "dddd" ) && strcmp ( struct_name , "ddd" ) ) {
LOGZ ( " reading parameter passed by reference. type: %s size: %i nsaa: %p\n" , type , ( int ) size , it -> nsaa ) ;
// passed on the stack
if ( target ! = NULL )
memcpy ( target , it -> nsaa , size ) ;
// increment stack pointer
it -> nsaa + = size ;
return ;
}
if ( * struct_name = = 0 ) {
* exception_gchandle = xamarin_create _mt _exception ( xamarin_strdup _printf ( PRODUCT ": Cannot marshal parameter type %s, collapsed type name is empty\n" , type ) ) ;
return ;
}
// passed in registers ( and on the stack if not enough registers )
LOGZ ( " reading parameter from registers/stack. type: %s (collapsed: %s) size: %i\n" , type , struct_name , ( int ) size ) ;
const char * t = struct_name ;
uint8_t * targ = ( uint8_t * ) target ;
do {
2019-12-12 06:17:29 +03:00
int c = param_read _primitive ( it , t , targ , size , exception_gchandle ) ;
2021-02-11 10:18:38 +03:00
if ( * exception_gchandle ! = INVALID_GCHANDLE )
2019-05-07 08:35:39 +03:00
return ;
if ( targ ! = NULL )
targ + = c ;
} while ( * + + t ) ;
}
static void
2021-02-11 10:18:38 +03:00
marshal_return _value ( void * context , const char * type , size_t size , void * vvalue , MonoType * mtype , bool retain , MonoMethod * method , MethodDescription * desc , GCHandle * exception_gchandle )
2019-05-07 08:35:39 +03:00
{
// COOP : accessing managed memory ( as input ) , so must be in unsafe mode .
MONO_ASSERT _GC _UNSAFE ;
char struct_name [ 5 ] ;
MonoObject * value = ( MonoObject * ) vvalue ;
struct ParamIterator * it = ( struct ParamIterator * ) context ;
LOGZ ( " marshalling return value %p as %s with size %i\n" , value , type , ( int ) size ) ;
switch ( type [ 0 ] ) {
case _C _FLT :
// single floating point return value
it -> state -> q0 . f . f1 = * ( float * ) mono_object _unbox ( value ) ;
break ;
case _C _DBL :
// double floating point return value
it -> state -> q0 . d = * ( double * ) mono_object _unbox ( value ) ;
break ;
case _C _STRUCT _B :
/ *
* Structures , this is ugly , but not as bad as x86_64 .
*
* A ) Structures of 1 to 4 fields of the same floating point type ( float or double ) , are passed in the q [ 0 -3 ] registers .
* B ) Any other structure > 16 bytes is passed by reference ( pointer to memory in x8 )
* C ) Structures <= 16 bytes are passed in x0 and x1 as needed . Each register can contain multiple fields .
* /
xamarin_collapse _struct _name ( type , struct_name , sizeof ( struct_name ) , exception_gchandle ) ;
2021-02-11 10:18:38 +03:00
if ( * exception_gchandle ! = INVALID_GCHANDLE )
2019-05-07 08:35:39 +03:00
return ;
if ( ( size = = 32 && ! strncmp ( struct_name , "dddd" , 4 ) ) ||
( size = = 24 && ! strncmp ( struct_name , "ddd" , 3 ) ) ||
( size = = 16 && ! strncmp ( struct_name , "dd" , 2 ) ) ||
( size = = 8 && ! strncmp ( struct_name , "d" , 1 ) ) ) {
LOGZ ( " marshalling as %i doubles (struct name: %s)\n" , ( int ) size / 8 , struct_name ) ;
double * ptr = ( double * ) mono_object _unbox ( value ) ;
2019-12-12 06:17:29 +03:00
for ( int i = 0 ; i < size / 8 ; i + + ) {
2019-05-07 08:35:39 +03:00
LOGZ ( " #%i: %f\n" , i , ptr [ i ] ) ;
it -> q [ i ] . d = ptr [ i ] ;
}
} else if ( ( size = = 16 && ! strncmp ( struct_name , "ffff" , 4 ) ) ||
( size = = 12 && ! strncmp ( struct_name , "fff" , 3 ) ) ||
( size = = 8 && ! strncmp ( struct_name , "ff" , 2 ) ) ||
( size = = 4 && ! strncmp ( struct_name , "f" , 1 ) ) ) {
LOGZ ( " marshalling as %i floats (struct name: %s)\n" , ( int ) size / 4 , struct_name ) ;
float * ptr = ( float * ) mono_object _unbox ( value ) ;
2019-12-12 06:17:29 +03:00
for ( int i = 0 ; i < size / 4 ; i + + ) {
2019-05-07 08:35:39 +03:00
LOGZ ( " #%i: %f\n" , i , ptr [ i ] ) ;
it -> q [ i ] . f . f1 = ptr [ i ] ;
}
} else if ( size > 16 ) {
LOGZ ( " marshalling as stret through x8\n" ) ;
memcpy ( ( void * ) it -> state -> x8 , mono_object _unbox ( value ) , size ) ;
} else {
LOGZ ( " marshalling as composite values in x0 and x1\n" ) ;
it -> state -> x0 = 0 ;
it -> state -> x1 = 0 ;
memcpy ( & it -> state -> x0 , mono_object _unbox ( value ) , size ) ;
}
break ;
// For primitive types we get a pointer to the actual value
case _C _BOOL : // signed char
case _C _CHR :
case _C _UCHR :
it -> state -> x0 = * ( uint8_t * ) mono_object _unbox ( value ) ;
break ;
case _C _SHT :
case _C _USHT :
it -> state -> x0 = * ( uint16_t * ) mono_object _unbox ( value ) ;
break ;
case _C _INT :
case _C _UINT :
it -> state -> x0 = * ( uint32_t * ) mono_object _unbox ( value ) ;
break ;
case _C _LNG :
case _C _ULNG :
case _C _LNG _LNG :
case _C _ULNG _LNG :
it -> state -> x0 = * ( uint64_t * ) mono_object _unbox ( value ) ;
break ;
// For pointer types we get the value itself .
case _C _CLASS :
case _C _SEL :
case _C _ID :
case _C _CHARPTR :
case _C _PTR :
if ( value = = NULL ) {
it -> state -> x0 = 0 ;
break ;
}
it -> state -> x0 = ( uint64_t ) xamarin_marshal _return _value ( it -> state -> sel ( ) , mtype , type , value , retain , method , desc , exception_gchandle ) ;
break ;
case _C _VOID :
break ;
case ' | ' : // direct pointer value
default :
if ( size = = 8 ) {
it -> state -> x0 = ( uint64_t ) value ;
} else {
* exception_gchandle = xamarin_create _mt _exception ( xamarin_strdup _printf ( PRODUCT ": Cannot marshal return type %s (size: %i)\n" , type , ( int ) size ) ) ;
}
break ;
}
}
void
xamarin_arch _trampoline ( struct XamarinCallState * state )
{
// COOP : called from ObjC , and does not access managed memory .
MONO_ASSERT _GC _SAFE ;
state -> _self = ( id ) state -> x0 ;
state -> _sel = ( SEL ) state -> x1 ;
dump_state ( state , "BEGIN: " ) ;
struct ParamIterator iter ;
iter . state = state ;
xamarin_invoke _trampoline ( ( enum TrampolineType ) state -> type , state -> self ( ) , state -> sel ( ) , param_iter _next , marshal_return _value , & iter ) ;
dump_state ( state , "END: " ) ;
}
# endif / * __arm64 __ * /