We’re upgrading a game that is currently live on PS4/5 and XB1/Scarlett from Unity 2020.3.35f1 to Unity 2022.3.14f1 and it started crashing when marshalling a class object from C# code into one of our C++ libraries as it attempts to do a memset on a nullptr (which wasn’t a nullptr before, as it was being marshalled correctly)
struct LPacket {
};
extern "C" __declspec(dllexport) void __cdecl GetPacket(LPacket *const dst, const int idx)
[StructLayout(LayoutKind.Sequential)]
public class LPacket {
}
[DllImport("xxxx")]
public static extern void GetPacket([Out] LPacket dst, int idx);
Something changed with IL2CPP and we’re not sure what. We know this because when building with the Mono backend our PC version works fine, the moment we switch to IL2CPP it starts crashing when calling the above method.
We’re in the process of narrowing down which Unity version started having this problem. In the meantime, any suggestion is welcome.
Edit: We’ve narrowed it down to this Editor version: Unity 2021.3.28
And this change in particular: IL2CPP: When P/Invoking with a blittable class parameter, pass a pinned pointer to the managed class to native. (UUM-33942)
We’re currently looking for a fix.
Thank you!
Edit2: We now know for sure that that is the fix that is causing our problems. Unity 2022.2.20f1 works correctly, meanwhle the subsequent version where the aforementioned issue was also fixed, 2022.2.21f1, also crashes in the same spot.
More evidence is in the il2cppOutput:
IL2CPP_EXTERN_C IL2CPP_METHOD_ATTR void XxxxDLL_GetPacket_m22748FF55FCB0E0EB59FB14005A23886C635B1E5 (LPacket_t49FC97AA2190BA893E8B524362C6068543588B9E* ___0_dst, int32_t ___1_idx, const RuntimeMethod* method)
{
typedef void (DEFAULT_CALL *PInvokeFunc) (LPacket_t49FC97AA2190BA893E8B524362C6068543588B9E_marshaled_pinvoke*, int32_t);
#if !FORCE_PINVOKE_INTERNAL && !FORCE_PINVOKE_xxxx_INTERNAL
static PInvokeFunc il2cppPInvokeFunc;
if (il2cppPInvokeFunc == NULL)
{
int parameterSize = sizeof(void*) + sizeof(int32_t);
il2cppPInvokeFunc = il2cpp_codegen_resolve_pinvoke<PInvokeFunc>(IL2CPP_NATIVE_STRING("xxxx"), "GetPacket", IL2CPP_CALL_DEFAULT, CHARSET_NOT_SPECIFIED, parameterSize, false);
IL2CPP_ASSERT(il2cppPInvokeFunc != NULL);
}
#endif
LPacket_t49FC97AA2190BA893E8B524362C6068543588B9E_marshaled_pinvoke ____0_dst_marshaled = {};
#if FORCE_PINVOKE_INTERNAL || FORCE_PINVOKE_xxxx_INTERNAL
reinterpret_cast<PInvokeFunc>(GetPacket)(___0_dst != NULL ? (&____0_dst_marshaled) : NULL, ___1_idx);
#else
il2cppPInvokeFunc(___0_dst != NULL ? (&____0_dst_marshaled) : NULL, ___1_idx);
#endif
if (___0_dst != NULL)
{
LPacket__ctor_mDC58D8B268C89FF8BEE55953B5D550F431C5383E(___0_dst, NULL);
LPacket_t49FC97AA2190BA893E8B524362C6068543588B9E_marshal_pinvoke_back(____0_dst_marshaled, *___0_dst);
}
if ((&____0_dst_marshaled) != NULL)
{
LPacket_t49FC97AA2190BA893E8B524362C6068543588B9E_marshal_pinvoke_cleanup(____0_dst_marshaled);
}
}
IL2CPP_EXTERN_C IL2CPP_METHOD_ATTR void XxxxDLL_GetPacket_m22748FF55FCB0E0EB59FB14005A23886C635B1E5 (LPacket_t49FC97AA2190BA893E8B524362C6068543588B9E* ___0_dst, int32_t ___1_idx, const RuntimeMethod* method)
{
typedef void (DEFAULT_CALL *PInvokeFunc) (void*, int32_t);
#if !FORCE_PINVOKE_INTERNAL && !FORCE_PINVOKE_xxxx_INTERNAL
static PInvokeFunc il2cppPInvokeFunc;
if (il2cppPInvokeFunc == NULL)
{
int parameterSize = sizeof(void*) + sizeof(int32_t);
il2cppPInvokeFunc = il2cpp_codegen_resolve_pinvoke<PInvokeFunc>(IL2CPP_NATIVE_STRING("xxxx"), "GetPacket", IL2CPP_CALL_DEFAULT, CHARSET_NOT_SPECIFIED, parameterSize, false);
IL2CPP_ASSERT(il2cppPInvokeFunc != NULL);
}
#endif
void* ____0_dst_marshaled = NULL;
#if FORCE_PINVOKE_INTERNAL || FORCE_PINVOKE_xxxx_INTERNAL
reinterpret_cast<PInvokeFunc>(GetPacket)(____0_dst_marshaled, ___1_idx);
#else
il2cppPInvokeFunc(____0_dst_marshaled, ___1_idx);
#endif
}
The line in particular that draws the attention is this void* ____0_dst_marshaled = NULL;