#include "il2cpp-config.h" #include "icalls/mscorlib/System.Runtime.InteropServices/Marshal.h" #include "il2cpp-class-internals.h" #include "il2cpp-tabledefs.h" #include "gc/GarbageCollector.h" #include "metadata/FieldLayout.h" #include "os/Atomic.h" #include "vm/Array.h" #include "vm/CCW.h" #include "vm/Class.h" #include "vm/Exception.h" #include "vm/Field.h" #include "vm/Image.h" #include "vm/LastError.h" #include "vm/MarshalAlloc.h" #include "vm/Object.h" #include "vm/PlatformInvoke.h" #include "vm/RCW.h" #include "vm/String.h" #include "vm/Type.h" #include "utils/MarshalingUtils.h" #include "utils/StringUtils.h" #include #include namespace il2cpp { namespace icalls { namespace mscorlib { namespace System { namespace Runtime { namespace InteropServices { int32_t Marshal::AddRefInternal(intptr_t pUnk) { return static_cast((void*)pUnk)->AddRef(); } intptr_t Marshal::AllocCoTaskMem(int32_t size) { intptr_t result; result = (intptr_t)vm::MarshalAlloc::Allocate(size); return result; } intptr_t Marshal::AllocHGlobal(intptr_t size) { intptr_t result; result = (intptr_t)vm::MarshalAlloc::AllocateHGlobal((size_t)size); return result; } void Marshal::copy_from_unmanaged(intptr_t source, int startIndex, Il2CppArray * destination, int length) { uint32_t element_size = (uint32_t)il2cpp_array_element_size(destination->klass); memcpy(il2cpp_array_addr_with_size(destination, element_size, startIndex), reinterpret_cast(source), length * element_size); } void Marshal::copy_to_unmanaged(Il2CppArray * source, int32_t startIndex, intptr_t destination, int32_t length) { uint32_t element_size = (uint32_t)il2cpp_array_element_size(source->klass); memcpy(reinterpret_cast(destination), il2cpp_array_addr_with_size(source, element_size, startIndex), length * element_size); } void Marshal::FreeBSTR(intptr_t ptr) { vm::PlatformInvoke::MarshalFreeBString(reinterpret_cast(ptr)); } void Marshal::FreeCoTaskMem(intptr_t ptr) { vm::MarshalAlloc::Free(reinterpret_cast(ptr)); } void Marshal::FreeHGlobal(intptr_t hglobal) { vm::MarshalAlloc::FreeHGlobal(reinterpret_cast(hglobal)); } bool Marshal::IsComObject(Il2CppObject* o) { if (o == NULL) vm::Exception::Raise(vm::Exception::GetArgumentNullException("o")); return o->klass->is_import_or_windows_runtime; } intptr_t Marshal::GetCCW(Il2CppObject* o, Il2CppReflectionType* T) { if (o == NULL) vm::Exception::Raise(vm::Exception::GetArgumentNullException("o")); if (T == NULL) vm::Exception::Raise(vm::Exception::GetArgumentNullException("T")); Il2CppClass* klass = vm::Class::FromIl2CppType(T->type); if (!vm::Class::IsInterface(klass)) { DECLARE_NATIVE_C_STRING_AS_STRING_VIEW_OF_IL2CPP_CHARS(parameter, IL2CPP_NATIVE_STRING("T")); DECLARE_NATIVE_C_STRING_AS_STRING_VIEW_OF_IL2CPP_CHARS(message, IL2CPP_NATIVE_STRING("The T parameter is not an interface.")); vm::Exception::Raise(vm::Exception::GetArgumentException(parameter, message)); } if (vm::Class::IsGeneric(klass)) { DECLARE_NATIVE_C_STRING_AS_STRING_VIEW_OF_IL2CPP_CHARS(parameter, IL2CPP_NATIVE_STRING("T")); DECLARE_NATIVE_C_STRING_AS_STRING_VIEW_OF_IL2CPP_CHARS(message, IL2CPP_NATIVE_STRING("The T parameter is a generic type.")); vm::Exception::Raise(vm::Exception::GetArgumentException(parameter, message)); } const Il2CppInteropData* interopData = klass->interopData; if (interopData == NULL || interopData->guid == NULL) { DECLARE_NATIVE_C_STRING_AS_STRING_VIEW_OF_IL2CPP_CHARS(parameter, IL2CPP_NATIVE_STRING("T")); DECLARE_NATIVE_C_STRING_AS_STRING_VIEW_OF_IL2CPP_CHARS(message, IL2CPP_NATIVE_STRING("The specified type must be visible from COM.")); vm::Exception::Raise(vm::Exception::GetArgumentException(parameter, message)); } return reinterpret_cast(vm::CCW::GetOrCreate(o, *interopData->guid)); } int32_t Marshal::GetComSlotForMethodInfoInternal(mscorlib_System_Reflection_MemberInfo * m) { NOT_SUPPORTED_IL2CPP(Marshal::GetComSlotForMethodInfoInternal, "Not implemented."); return 0; } Il2CppDelegate* Marshal::GetDelegateForFunctionPointerInternal(intptr_t ptr, Il2CppReflectionType* t) { Il2CppClass* delegateType = il2cpp::vm::Class::FromIl2CppType(t->type); return vm::PlatformInvoke::MarshalFunctionPointerToDelegate(reinterpret_cast(ptr), delegateType); } intptr_t Marshal::GetFunctionPointerForDelegateInternal(Il2CppDelegate* d) { return vm::PlatformInvoke::MarshalDelegate(d); } intptr_t Marshal::GetIDispatchForObjectInternal(Il2CppObject* o) { NOT_SUPPORTED_IL2CPP(Marshal::GetIDispatchForObjectInternal, "Not implemented."); return 0; } intptr_t Marshal::GetIUnknownForObjectInternal(Il2CppObject* o) { NOT_SUPPORTED_IL2CPP(Marshal::GetIUnknownForObjectInternal, "This icall is not supported by il2cpp. Use the il2cpp_codegen_com_get_iunknown_for_object intrinsic instead."); return 0; } Il2CppObject* Marshal::GetObjectForCCW(intptr_t pUnk) { if (pUnk == 0) return NULL; return vm::RCW::GetOrCreateFromIUnknown(reinterpret_cast(pUnk), il2cpp_defaults.il2cpp_com_object_class); } Il2CppString* Marshal::PtrToStringBSTR(intptr_t ptr) { if (ptr == 0) vm::Exception::Raise(vm::Exception::GetArgumentNullException("ptr")); return vm::PlatformInvoke::MarshalCppBStringToCSharpStringResult(reinterpret_cast(ptr)); } Il2CppString* Marshal::PtrToStringAnsi_mscorlib_System_String_mscorlib_System_IntPtr(intptr_t ptr) { char* value = (char*)ptr; if (value == NULL) return NULL; return vm::String::New(value); } Il2CppString* Marshal::PtrToStringAnsi_mscorlib_System_String_mscorlib_System_IntPtr_mscorlib_System_Int32(intptr_t ptr, int32_t len) { char* value = (char*)ptr; if (value == NULL) vm::Exception::Raise(vm::Exception::GetArgumentNullException("ptr")); return vm::String::NewLen(value, len); } Il2CppString* Marshal::PtrToStringUni_mscorlib_System_String_mscorlib_System_IntPtr(intptr_t ptr) { Il2CppChar* value = reinterpret_cast(ptr); if (value == NULL) return NULL; int32_t len = 0; Il2CppChar* t = value; while (*t++) len++; return vm::String::NewUtf16(value, len); } Il2CppString* Marshal::PtrToStringUni_mscorlib_System_String_mscorlib_System_IntPtr_mscorlib_System_Int32(intptr_t ptr, int32_t len) { Il2CppChar* value = reinterpret_cast(ptr); if (value == NULL) vm::Exception::Raise(vm::Exception::GetArgumentNullException("ptr")); return vm::String::NewUtf16(value, len); } Il2CppObject* Marshal::PtrToStructure(intptr_t ptr, Il2CppReflectionType* structureType) { if (ptr == 0) return NULL; if (structureType == NULL) vm::Exception::Raise(vm::Exception::GetArgumentNullException("structureType")); Il2CppClass* type = vm::Class::FromIl2CppType(structureType->type); Il2CppTypeEnum typeType = structureType->type->type; if (typeType == IL2CPP_TYPE_STRING || typeType == IL2CPP_TYPE_SZARRAY || (typeType == IL2CPP_TYPE_CLASS && !vm::Class::HasDefaultConstructor(type))) { vm::Exception::Raise(vm::Exception::GetMissingMethodException("No parameterless constructor defined for this object.")); } if (type->interopData != NULL && type->interopData->pinvokeMarshalFromNativeFunction != NULL) { Il2CppObject* result = vm::Object::New(type); if (typeType == IL2CPP_TYPE_CLASS) { typedef void (*Constructor)(Il2CppObject*); Constructor ctor = reinterpret_cast(vm::Class::GetMethodFromName(type, ".ctor", 0)->methodPointer); ctor(result); utils::MarshalingUtils::MarshalStructFromNative(reinterpret_cast(ptr), result, type->interopData); } else { utils::MarshalingUtils::MarshalStructFromNative(reinterpret_cast(ptr), vm::Object::Unbox(result), type->interopData); } return result; } // If there's no custom marshal function, it means it's either a primitive, or invalid argument if (type->native_size != -1) { // We may also need to throw a NotSupportedException for an ArgIterator. if (structureType->type->type == IL2CPP_TYPE_VOID) vm::Exception::Raise(vm::Exception::GetNotSupportedException("Cannot dynamically create an instance of System.Void.")); // PtrToStructure is supposed to throw on enums if (!type->enumtype) { Il2CppObject* result = vm::Object::New(type); memcpy(vm::Object::Unbox(result), reinterpret_cast(ptr), type->native_size); return result; } } // If we got this far, throw an exception if (type->generic_class != NULL || type->is_generic) vm::Exception::Raise(vm::Exception::GetArgumentException("structure", "The specified object must not be an instance of a generic type.")); vm::Exception::Raise(vm::Exception::GetArgumentException("structure", "The specified structure must be blittable or have layout information.")); return NULL; } void Marshal::PtrToStructureObject(intptr_t ptr, Il2CppObject* structure) { if (ptr == 0) return; if (structure == NULL) vm::Exception::Raise(vm::Exception::GetArgumentNullException("structure")); Il2CppClass* type = structure->klass; // This is only legal for classes. if (type->byval_arg.type != IL2CPP_TYPE_CLASS) { vm::Exception::Raise(vm::Exception::GetArgumentException("structure", "The specified structure must be an instance of a formattable class.")); } if (type->interopData != NULL && type->interopData->pinvokeMarshalFromNativeFunction != NULL) { utils::MarshalingUtils::MarshalStructFromNative(reinterpret_cast(ptr), structure, type->interopData); return; } if (type->generic_class || type->is_generic) vm::Exception::Raise(vm::Exception::GetArgumentException("structure", "The specified object must not be an instance of a generic type.")); vm::Exception::Raise(vm::Exception::GetArgumentException("structure", "The specified structure must be blittable or have layout information.")); } int32_t Marshal::QueryInterfaceInternal(intptr_t pUnk, mscorlib_System_Guid * iid, intptr_t* ppv) { Il2CppIUnknown* unknown = reinterpret_cast(pUnk); return unknown->QueryInterface(reinterpret_cast(*iid), reinterpret_cast(ppv)); } template static inline T ReadValue(intptr_t ptr, int32_t offset) { return *reinterpret_cast(reinterpret_cast(ptr) + offset); } uint8_t Marshal::ReadByte(intptr_t ptr, int32_t ofs) { return ReadValue(ptr, ofs); } int16_t Marshal::ReadInt16(intptr_t ptr, int32_t ofs) { return ReadValue(ptr, ofs); } int32_t Marshal::ReadInt32(intptr_t ptr, int32_t ofs) { return ReadValue(ptr, ofs); } int64_t Marshal::ReadInt64(intptr_t ptr, int32_t ofs) { return ReadValue(ptr, ofs); } intptr_t Marshal::ReadIntPtr(intptr_t ptr, int32_t ofs) { return ReadValue(ptr, ofs); } int32_t Marshal::ReleaseInternal(intptr_t pUnk) { return reinterpret_cast(pUnk)->Release(); } int32_t Marshal::ReleaseComObjectInternal(Il2CppObject* co) { // There's a check in mscorlib before calling this internal icall, so assert instead of full check is OK here. IL2CPP_ASSERT(co->klass->is_import_or_windows_runtime); int32_t newRefCount = os::Atomic::Decrement(&static_cast(co)->refCount); if (newRefCount == 0) { // We can't really release the COM object directly, because it might have additional // fields that cache different interfaces. So let's just call its finalizer here. // In order to deal with the fact that this may get called from different threads // at the same time, we (atomically) register a NULL finalizer, and if another finalizer // was already registered, we call it. If there was no finalizer registered, it means // that we lost the race and we should just carry on. gc::GarbageCollector::FinalizerCallback oldFinalizer = gc::GarbageCollector::RegisterFinalizerWithCallback(co, NULL); if (oldFinalizer != NULL) oldFinalizer(co, NULL); } return newRefCount; } int Marshal::SizeOf(Il2CppReflectionType* rtype) { if (rtype == NULL) vm::Exception::Raise(vm::Exception::GetArgumentNullException("t")); Il2CppClass* typeInfo = vm::Class::FromIl2CppType(rtype->type); il2cpp::vm::Class::SetupFields(typeInfo); if (typeInfo->native_size != -1) { // SizeOf "cannot" get size of enums and strings if (!typeInfo->enumtype && rtype->type->type != IL2CPP_TYPE_STRING) return typeInfo->native_size; } // If we get to here, type does not have a native size // So let's figure what kind of exception to throw if (typeInfo->generic_class != NULL || typeInfo->is_generic) vm::Exception::Raise(vm::Exception::GetArgumentException("t", "The t parameter is a generic type.")); std::string exceptionMessage = utils::StringUtils::Printf("Type \'%s\' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed.", vm::Type::GetName(rtype->type, IL2CPP_TYPE_NAME_FORMAT_FULL_NAME).c_str()); vm::Exception::Raise(vm::Exception::GetArgumentException(NULL, exceptionMessage.c_str())); return 0; } intptr_t Marshal::StringToBSTR(Il2CppString* s) { return reinterpret_cast(vm::PlatformInvoke::MarshalCSharpStringToCppBString(s)); } intptr_t Marshal::StringToHGlobalUni(Il2CppString* s) { if (s == NULL) return 0; int32_t size = utils::StringUtils::GetLength(s); const Il2CppChar* utf16 = utils::StringUtils::GetChars(s); size_t bytes = (size + 1) * 2; Il2CppChar* cstr = static_cast(vm::MarshalAlloc::AllocateHGlobal(bytes)); memcpy(cstr, utf16, bytes); return reinterpret_cast(cstr); } intptr_t Marshal::StringToHGlobalAnsi(Il2CppString* s) { if (s == NULL) return 0; const Il2CppChar* utf16 = utils::StringUtils::GetChars(s); std::string str = il2cpp::utils::StringUtils::Utf16ToUtf8(utf16); char *cstr = (char*)vm::MarshalAlloc::AllocateHGlobal(str.size() + 1); strcpy(cstr, str.c_str()); return reinterpret_cast(cstr); } void Marshal::StructureToPtr(Il2CppObject* structure, intptr_t ptr, bool deleteOld) { if (structure == NULL) vm::Exception::Raise(vm::Exception::GetArgumentNullException("structure")); if (ptr == 0) vm::Exception::Raise(vm::Exception::GetArgumentNullException("ptr")); Il2CppClass* type = structure->klass; if (type->interopData != NULL && type->interopData->pinvokeMarshalToNativeFunction != NULL) { if (deleteOld) utils::MarshalingUtils::MarshalFreeStruct(reinterpret_cast(ptr), type->interopData); void* objectPtr = (type->byval_arg.type == IL2CPP_TYPE_CLASS) ? structure : vm::Object::Unbox(structure); utils::MarshalingUtils::MarshalStructToNative(objectPtr, reinterpret_cast(ptr), type->interopData); return; } // If there's no custom marshal function, it means it's either a primitive, or invalid argument if (type->native_size != -1) { // StructureToPtr is supposed to throw on strings and enums if (!type->enumtype && type->byval_arg.type != IL2CPP_TYPE_STRING) { memcpy(reinterpret_cast(ptr), vm::Object::Unbox(structure), type->native_size); return; } } // If we got this far, throw an exception Il2CppException* exception; if (type->generic_class != NULL || type->is_generic) { exception = vm::Exception::GetArgumentException("structure", "The specified object must not be an instance of a generic type."); } else { exception = vm::Exception::GetArgumentException("structure", "The specified structure must be blittable or have layout information."); } vm::Exception::Raise(exception); } template static inline void WriteValue(intptr_t ptr, int32_t offset, T value) { *reinterpret_cast(reinterpret_cast(ptr) + offset) = value; } void Marshal::WriteByte(intptr_t ptr, int32_t ofs, uint8_t val) { WriteValue(ptr, ofs, val); } void Marshal::WriteInt16(intptr_t ptr, int32_t ofs, int16_t val) { WriteValue(ptr, ofs, val); } void Marshal::WriteInt32(intptr_t ptr, int32_t ofs, int32_t val) { WriteValue(ptr, ofs, val); } void Marshal::WriteInt64(intptr_t ptr, int32_t ofs, int64_t val) { WriteValue(ptr, ofs, val); } void Marshal::WriteIntPtr(intptr_t ptr, int32_t ofs, intptr_t val) { WriteValue(ptr, ofs, val); } void Marshal::DestroyStructure(intptr_t ptr, Il2CppReflectionType* structureType) { if (ptr == 0) vm::Exception::Raise(vm::Exception::GetArgumentNullException("ptr")); if (structureType == NULL) vm::Exception::Raise(vm::Exception::GetArgumentNullException("structureType")); Il2CppClass* type = vm::Class::FromIl2CppType(structureType->type); // If cleanup function exists, it will call it and return true // In that case, we're done. if (utils::MarshalingUtils::MarshalFreeStruct(reinterpret_cast(ptr), type->interopData)) return; if (type->is_generic) { vm::Exception::Raise(vm::Exception::GetArgumentException("structureType", "The specified type must not be an instance of a generic type.")); } // Enums are blittable, but they don't have layout information, therefore Marshal.DestroyStructure is supposed to throw if (!type->enumtype) { // Char and Boolean are not blittable, but they should not raise this exception, as we can call DestroyStructure on them without problems. if (type->is_blittable || structureType->type->type == IL2CPP_TYPE_CHAR || structureType->type->type == IL2CPP_TYPE_BOOLEAN) return; } // If we got this far, throw an exception vm::Exception::Raise(vm::Exception::GetArgumentException("structureType", "The specified type must be blittable or have layout information.")); } int32_t Marshal::GetLastWin32Error() { return vm::LastError::GetLastError(); } static size_t RoundUpToMultiple(size_t numToRound, size_t multiple) { if (multiple == 0) return numToRound; size_t remainder = numToRound % multiple; if (remainder == 0) return numToRound; return numToRound + multiple - remainder; } intptr_t Marshal::OffsetOf(Il2CppReflectionType* t, Il2CppString* fieldName) { std::string fieldNameToFind = utils::StringUtils::Utf16ToUtf8(fieldName->chars); Il2CppClass* type = vm::Class::FromIl2CppType(t->type); FieldInfo* field = vm::Class::GetFieldFromName(type, fieldNameToFind.c_str()); if (field == NULL || (vm::Field::GetFlags(field) & FIELD_ATTRIBUTE_STATIC)) { std::string message; message = "Field '" + fieldNameToFind + "' is not a marshaled member of the type '" + type->name + "'"; vm::Exception::Raise(vm::Exception::GetArgumentException("fieldName", message.c_str())); } // Order the base classes so the most base class is first. std::deque inheritanceHierarchy; while (type) { inheritanceHierarchy.push_front(type); type = type->parent; } bool fieldFound = false; size_t offset = 0; FieldInfo* previousField = NULL; for (std::deque::iterator it = inheritanceHierarchy.begin(); it < inheritanceHierarchy.end(); ++it) { type = *it; void* iter = NULL; while ((field = vm::Class::GetFields(type, &iter))) { if (vm::Field::GetFlags(field) & FIELD_ATTRIBUTE_STATIC) continue; // Determine how much the previous field added to the offset. if (previousField != NULL) { if (!vm::Type::IsStruct(previousField->type)) { size_t managedOffset = field->offset - previousField->offset; if (managedOffset != 0) // overlapping fields have a zero offset { offset += vm::Class::GetFieldMarshaledSize(previousField); } } else { offset += vm::Class::FromIl2CppType(previousField->type)->native_size; } if (offset != 0) { int marshaledFieldAlignment = vm::Class::GetFieldMarshaledAlignment(field); offset = RoundUpToMultiple(offset, type->packingSize == 0 ? marshaledFieldAlignment : std::min((int)type->packingSize, marshaledFieldAlignment)); } } previousField = field; if (fieldNameToFind == vm::Field::GetName(field)) { fieldFound = true; break; } } if (fieldFound) break; } return static_cast(offset); } // We _could_ implement these two if we wanted - we'd just need to init their metadata void Marshal::Prelink(Il2CppReflectionMethod* m) { NOT_SUPPORTED_IL2CPP(Marshal::Prelink, "Not implemented."); } void Marshal::PrelinkAll(Il2CppReflectionType* c) { NOT_SUPPORTED_IL2CPP(Marshal::PrelinkAll, "Not implemented."); } intptr_t Marshal::ReAllocCoTaskMem(intptr_t ptr, int32_t size) { return reinterpret_cast(vm::MarshalAlloc::ReAlloc(reinterpret_cast(ptr), size)); } intptr_t Marshal::ReAllocHGlobal(intptr_t ptr, intptr_t size) { if (ptr == 0) vm::Exception::RaiseOutOfMemoryException(); return reinterpret_cast(vm::MarshalAlloc::ReAllocHGlobal(reinterpret_cast(ptr), (size_t)size)); } intptr_t Marshal::UnsafeAddrOfPinnedArrayElement(Il2CppArray* arr, int32_t index) { return reinterpret_cast(il2cpp_array_addr_with_size(arr, il2cpp_array_element_size(arr->klass), index)); } intptr_t Marshal::BufferToBSTR(Il2CppArray* ptr, int32_t slen) { IL2CPP_NOT_IMPLEMENTED_ICALL(Marshal::BufferToBSTR); IL2CPP_UNREACHABLE; } int32_t Marshal::GetHRForException_WinRT(Il2CppException* e) { IL2CPP_NOT_IMPLEMENTED_ICALL(Marshal::GetHRForException_WinRT); IL2CPP_UNREACHABLE; } intptr_t Marshal::GetRawIUnknownForComObjectNoAddRef(Il2CppObject* o) { IL2CPP_ASSERT(o->klass->is_import_or_windows_runtime); return reinterpret_cast(static_cast(o)->identity); } Il2CppObject* Marshal::GetNativeActivationFactory(Il2CppObject* type) { IL2CPP_NOT_IMPLEMENTED_ICALL(Marshal::GetNativeActivationFactory); IL2CPP_UNREACHABLE; } intptr_t Marshal::AllocCoTaskMemSize(intptr_t sizet) { intptr_t result; result = (intptr_t)vm::MarshalAlloc::Allocate(sizet); return result; } } /* namespace InteropServices */ } /* namespace Runtime */ } /* namespace System */ } /* namespace mscorlib */ } /* namespace icalls */ } /* namespace il2cpp */