Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Bug IL2CPP: Class object interop marshalling fails

Discussion in 'Editor & General Support' started by AmberStudio_XboxDev, Nov 29, 2023.

  1. AmberStudio_XboxDev

    AmberStudio_XboxDev

    Joined:
    Feb 14, 2019
    Posts:
    4
    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)


    Code (C++):
    1.  
    2. struct LPacket {
    3. };
    4.  
    5. extern "C" __declspec(dllexport) void __cdecl GetPacket(LPacket *const dst, const int idx)
    6.  
    Code (CSharp):
    1.  
    2. [StructLayout(LayoutKind.Sequential)]
    3. public class LPacket {
    4. }
    5. [DllImport("xxxx")]
    6.     public static extern void GetPacket([Out] LPacket dst, int idx);
    7.  

    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: https://unity.com/releases/editor/whats-new/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:

    Code (2022.2.20):
    1.  
    2.  
    3. IL2CPP_EXTERN_C IL2CPP_METHOD_ATTR void XxxxDLL_GetPacket_m22748FF55FCB0E0EB59FB14005A23886C635B1E5 (LPacket_t49FC97AA2190BA893E8B524362C6068543588B9E* ___0_dst, int32_t ___1_idx, const RuntimeMethod* method)
    4. {
    5.  
    6.     typedef void (DEFAULT_CALL *PInvokeFunc) (LPacket_t49FC97AA2190BA893E8B524362C6068543588B9E_marshaled_pinvoke*, int32_t);
    7.     #if !FORCE_PINVOKE_INTERNAL && !FORCE_PINVOKE_xxxx_INTERNAL
    8.     static PInvokeFunc il2cppPInvokeFunc;
    9.     if (il2cppPInvokeFunc == NULL)
    10.     {
    11.         int parameterSize = sizeof(void*) + sizeof(int32_t);
    12.         il2cppPInvokeFunc = il2cpp_codegen_resolve_pinvoke<PInvokeFunc>(IL2CPP_NATIVE_STRING("xxxx"), "GetPacket", IL2CPP_CALL_DEFAULT, CHARSET_NOT_SPECIFIED, parameterSize, false);
    13.         IL2CPP_ASSERT(il2cppPInvokeFunc != NULL);
    14.     }
    15.     #endif
    16.  
    17.     LPacket_t49FC97AA2190BA893E8B524362C6068543588B9E_marshaled_pinvoke ____0_dst_marshaled = {};
    18.  
    19.     #if FORCE_PINVOKE_INTERNAL || FORCE_PINVOKE_xxxx_INTERNAL
    20.     reinterpret_cast<PInvokeFunc>(GetPacket)(___0_dst != NULL ? (&____0_dst_marshaled) : NULL, ___1_idx);
    21.     #else
    22.     il2cppPInvokeFunc(___0_dst != NULL ? (&____0_dst_marshaled) : NULL, ___1_idx);
    23.     #endif
    24.  
    25.     if (___0_dst != NULL)
    26.     {
    27.         LPacket__ctor_mDC58D8B268C89FF8BEE55953B5D550F431C5383E(___0_dst, NULL);
    28.         LPacket_t49FC97AA2190BA893E8B524362C6068543588B9E_marshal_pinvoke_back(____0_dst_marshaled, *___0_dst);
    29.     }
    30.  
    31.     if ((&____0_dst_marshaled) != NULL)
    32.     {
    33.         LPacket_t49FC97AA2190BA893E8B524362C6068543588B9E_marshal_pinvoke_cleanup(____0_dst_marshaled);
    34.     }
    35.  
    36. }
    37.  
    Code (2022.2.21):
    1.  
    2. IL2CPP_EXTERN_C IL2CPP_METHOD_ATTR void XxxxDLL_GetPacket_m22748FF55FCB0E0EB59FB14005A23886C635B1E5 (LPacket_t49FC97AA2190BA893E8B524362C6068543588B9E* ___0_dst, int32_t ___1_idx, const RuntimeMethod* method)
    3. {
    4.  
    5.  
    6.     typedef void (DEFAULT_CALL *PInvokeFunc) (void*, int32_t);
    7.     #if !FORCE_PINVOKE_INTERNAL && !FORCE_PINVOKE_xxxx_INTERNAL
    8.     static PInvokeFunc il2cppPInvokeFunc;
    9.     if (il2cppPInvokeFunc == NULL)
    10.     {
    11.         int parameterSize = sizeof(void*) + sizeof(int32_t);
    12.         il2cppPInvokeFunc = il2cpp_codegen_resolve_pinvoke<PInvokeFunc>(IL2CPP_NATIVE_STRING("xxxx"), "GetPacket", IL2CPP_CALL_DEFAULT, CHARSET_NOT_SPECIFIED, parameterSize, false);
    13.         IL2CPP_ASSERT(il2cppPInvokeFunc != NULL);
    14.     }
    15.     #endif
    16.  
    17.     void* ____0_dst_marshaled = NULL;
    18.  
    19.     #if FORCE_PINVOKE_INTERNAL || FORCE_PINVOKE_xxxx_INTERNAL
    20.     reinterpret_cast<PInvokeFunc>(GetPacket)(____0_dst_marshaled, ___1_idx);
    21.     #else
    22.     il2cppPInvokeFunc(____0_dst_marshaled, ___1_idx);
    23.     #endif
    24.  
    25. }
    26.  
    The line in particular that draws the attention is this
    void* ____0_dst_marshaled = NULL;
     
    Last edited: Nov 29, 2023
    evanratt and AM_maki like this.
  2. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,707
    Thanks for finding this! Can you by any chance file a bug report through the editor? Via the Help -> Report a Bug button. That way we can be sure we fix the issue affecting your code (as we'll be able to test it after the fix) and we'll be able to notify you when the fix goes out.