Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Off by 256 error in generated IL code for label. #21799

Open
manjaroman2 opened this issue Sep 9, 2024 · 1 comment
Open

Off by 256 error in generated IL code for label. #21799

manjaroman2 opened this issue Sep 9, 2024 · 1 comment

Comments

@manjaroman2
Copy link

manjaroman2 commented Sep 9, 2024

The code:

public static OneParameter<ulong, Item> GenerateILGetRankIL()
{
    DynamicMethod dynamicMethod = new(
        "getRank",
        typeof(ulong),
        new[] { typeof(Item) }
    );

    ILGenerator il = dynamicMethod.GetILGenerator();
    Label[] labels = new Label[8];
    for (int i = 0; i < labels.Length; i++) labels[i] = il.DefineLabel();

    // ulong num = 0uL;
    il.Emit(OpCodes.Ldc_I4_0);
    il.Emit(OpCodes.Conv_I8);
    il.Emit(OpCodes.Stloc_0);
    // if (item == null)
    il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Brtrue_S, labels[0]);
    // return num;
    il.Emit(OpCodes.Ldloc_0);
    il.Emit(OpCodes.Ret);
    // ulong num2 = 9223372036854775808uL;
    il.MarkLabel(labels[0]);
    il.Emit(OpCodes.Ldc_I8, -9223372036854775808);
    il.Emit(OpCodes.Stloc_1);
    // int num3 = 64;
    il.Emit(OpCodes.Ldc_I4_S, 64);
    il.Emit(OpCodes.Stloc_2);


    // if (item.IsUnique)
    il.Emit(OpCodes.Ldarg_0);
    il.EmitCall(OpCodes.Callvirt, Item__GetUnique, null);
    il.Emit(OpCodes.Brfalse_S, labels[1]);
    // num |= num2;
    il.Emit(OpCodes.Ldloc_0);
    il.Emit(OpCodes.Ldloc_1);
    il.Emit(OpCodes.Or);
    il.Emit(OpCodes.Stloc_0);
    // num2 >>= 1;
    il.MarkLabel(labels[1]);
    il.Emit(OpCodes.Ldloc_1);
    il.Emit(OpCodes.Ldc_I4_1);
    il.Emit(OpCodes.Shr_Un);
    il.Emit(OpCodes.Stloc_1);
    // num3--;
    il.Emit(OpCodes.Ldloc_2);
    il.Emit(OpCodes.Ldc_I4_1);
    il.Emit(OpCodes.Sub);
    il.Emit(OpCodes.Stloc_2);


    // num |= num2 >> (int)(6 - item.Rarity);
    il.Emit(OpCodes.Ldloc_0);
    il.Emit(OpCodes.Ldloc_1);
    il.Emit(OpCodes.Ldc_I4_6);
    il.Emit(OpCodes.Ldarg_0);
    il.EmitCall(OpCodes.Callvirt, Item__GetRarity, null);
    il.Emit(OpCodes.Sub);
    il.Emit(OpCodes.Ldc_I4_S, 63);
    il.Emit(OpCodes.And);
    il.Emit(OpCodes.Shr_Un);
    il.Emit(OpCodes.Or);
    il.Emit(OpCodes.Stloc_0);
    // num2 >>= 6;
    il.Emit(OpCodes.Ldloc_1);
    il.Emit(OpCodes.Ldc_I4_6);
    il.Emit(OpCodes.Shr_Un);
    il.Emit(OpCodes.Stloc_1);
    // num3 -= 6;
    il.Emit(OpCodes.Ldloc_2);
    il.Emit(OpCodes.Ldc_I4_6);
    il.Emit(OpCodes.Sub);
    il.Emit(OpCodes.Stloc_2);


    // num |= num2 >> 5 - item.Tier.Id;
    il.Emit(OpCodes.Ldloc_0);
    il.Emit(OpCodes.Ldloc_1);
    il.Emit(OpCodes.Ldc_I4_5);
    il.Emit(OpCodes.Ldarg_0);
    il.EmitCall(OpCodes.Callvirt, Item__GetTier, null);
    il.Emit(OpCodes.Stloc_3);
    il.Emit(OpCodes.Ldloca_S, 3);
    il.EmitCall(OpCodes.Callvirt, TierId__GetId, null);
    il.Emit(OpCodes.Sub);
    il.Emit(OpCodes.Ldc_I4_S, 63);
    il.Emit(OpCodes.And);
    il.Emit(OpCodes.Shr_Un);
    il.Emit(OpCodes.Or);
    il.Emit(OpCodes.Stloc_0);
    // num2 >>= 5;
    il.Emit(OpCodes.Ldloc_1);
    il.Emit(OpCodes.Ldc_I4_5);
    il.Emit(OpCodes.Shr_Un);
    il.Emit(OpCodes.Stloc_1);
    // num3 -= 5;
    il.Emit(OpCodes.Ldloc_2);
    il.Emit(OpCodes.Ldc_I4_5);
    il.Emit(OpCodes.Sub);
    il.Emit(OpCodes.Stloc_2);


    // num |= num2 >> (int)item.Type;
    il.Emit(OpCodes.Ldloc_0);
    il.Emit(OpCodes.Ldloc_1);
    il.Emit(OpCodes.Ldarg_0);
    il.EmitCall(OpCodes.Callvirt, Item__GetType, null);
    il.Emit(OpCodes.Ldc_I4_S, 63);
    il.Emit(OpCodes.And);
    il.Emit(OpCodes.Shr_Un);
    il.Emit(OpCodes.Or);
    // num2 >>= 11;
    il.Emit(OpCodes.Stloc_0);
    il.Emit(OpCodes.Ldloc_1);
    il.Emit(OpCodes.Ldc_I4_S, 11);
    il.Emit(OpCodes.Shr_Un);
    il.Emit(OpCodes.Stloc_1);
    // num3 -= 11;
    il.Emit(OpCodes.Ldloc_2);
    il.Emit(OpCodes.Ldc_I4_S, 11);
    il.Emit(OpCodes.Sub);
    il.Emit(OpCodes.Stloc_2);
    // num3 -= 26;
    il.Emit(OpCodes.Ldloc_2);
    il.Emit(OpCodes.Ldc_I4_S, 26);
    il.Emit(OpCodes.Sub);
    il.Emit(OpCodes.Stloc_2);


    // string subtypeCode = item.SubtypeCode;
    il.Emit(OpCodes.Ldarg_0);
    il.EmitCall(OpCodes.Callvirt, Item__GetSubtypeCode, null);
    il.Emit(OpCodes.Stloc_S, 4);
    // (no C# code)
    il.Emit(OpCodes.Ldc_I4_0);
    il.Emit(OpCodes.Stloc_S, 5);
    // 	foreach (int num4 in subtypeCode)
    // 	{
    // 		int num5 = num4;
    // 		if (num5 < 65)
    // 		{
    // 			continue;
    // 		}
    // 		if (num4 > 65 + num3)
    // 		{
    // 			if (num5 > 96 && num5 < 123)
    // 			{
    // 				num ^= num2 >> num4 - 97 + num3;
    // 			}
    // 		}
    // 		else
    // 		{
    // 			num ^= num2 >> num4 - 65;
    // 		}
    // 	}
    il.Emit(OpCodes.Br_S, labels[7]);
    MelonLogger.Msg($"MarkLabel 2 {il.ILOffset}"); 
    il.MarkLabel(labels[2]);
    MelonLogger.Msg($"MarkLabel 2 {il.ILOffset}"); 
    il.Emit(OpCodes.Ldloc_S, 4);
    il.Emit(OpCodes.Ldloc_S, 5);
    il.EmitCall(OpCodes.Callvirt,String__GetChars, null);
    il.Emit(OpCodes.Stloc_S, 6);
    // int num5 = num4;
    il.Emit(OpCodes.Ldloc_S, 6);
    il.Emit(OpCodes.Stloc_S, 7);
    // if (num5 < 65)
    il.Emit(OpCodes.Ldloc_S, 7);
    il.Emit(OpCodes.Ldc_I4_S, 65);
    il.Emit(OpCodes.Bge_S, labels[4]);
    // if (num5 > 96 && num5 < 123)
    il.Emit(OpCodes.Br_S, labels[6]);
    // loop start (head: IL_00b0)
    il.MarkLabel(labels[3]);
    il.Emit(OpCodes.Ldloc_S, 7);
    il.Emit(OpCodes.Ldc_I4_S, 96);
    il.Emit(OpCodes.Ble_S, labels[6]);
    il.Emit(OpCodes.Ldloc_S, 7);
    il.Emit(OpCodes.Ldc_I4_S, 123);
    MelonLogger.Msg($"Blt_S {il.ILOffset}"); 
    il.Emit(OpCodes.Blt_S, labels[5]);
    MelonLogger.Msg($"Blt_S {il.ILOffset}"); 
    
    // if (num4 > 65 + num3)
    il.Emit(OpCodes.Br_S, labels[6]);
    il.MarkLabel(labels[4]);
    il.Emit(OpCodes.Ldloc_S, 6);
    il.Emit(OpCodes.Ldc_I4_S, 65);
    il.Emit(OpCodes.Ldloc_2);
    il.Emit(OpCodes.Add);
    il.Emit(OpCodes.Bgt_S, labels[3]);
    // end loop
    // num ^= num2 >> num4 - 65;
    il.Emit(OpCodes.Ldloc_0);
    il.Emit(OpCodes.Ldloc_1);
    il.Emit(OpCodes.Ldloc_S, 6);
    il.Emit(OpCodes.Ldc_I4_S, 65);
    il.Emit(OpCodes.Sub);
    il.Emit(OpCodes.Ldc_I4_S, 63);
    il.Emit(OpCodes.And);
    il.Emit(OpCodes.Shr_Un);
    il.Emit(OpCodes.Xor);
    il.Emit(OpCodes.Stloc_0);
    // num ^= num2 >> num4 - 97 + num3;
    il.Emit(OpCodes.Br_S, labels[6]);
    MelonLogger.Msg($"MarkLabel 5 {il.ILOffset}"); 
    il.MarkLabel(labels[5]);
    MelonLogger.Msg($"MarkLabel 5 {il.ILOffset}"); 
    il.Emit(OpCodes.Ldloc_0);
    il.Emit(OpCodes.Ldloc_1);
    il.Emit(OpCodes.Ldloc_S, 6);
    il.Emit(OpCodes.Ldc_I4_S, 97);
    il.Emit(OpCodes.Sub);
    il.Emit(OpCodes.Ldloc_2);
    il.Emit(OpCodes.Add);
    il.Emit(OpCodes.Ldc_I4_S, 63);
    il.Emit(OpCodes.And);
    il.Emit(OpCodes.Shr_Un);
    il.Emit(OpCodes.Xor);
    il.Emit(OpCodes.Stloc_0);
    // (no C# code)
    il.MarkLabel(labels[6]);
    il.Emit(OpCodes.Ldloc_S, 5);
    il.Emit(OpCodes.Ldc_I4_1);
    il.Emit(OpCodes.Add);
    il.Emit(OpCodes.Stloc_S, 5);
    // return num;
    il.MarkLabel(labels[7]);
    il.Emit(OpCodes.Ldloc_S, 5);
    il.Emit(OpCodes.Ldloc_S, 4);
    il.EmitCall(OpCodes.Callvirt, String__GetLength, null);
    MelonLogger.Msg($"Blt_S {il.ILOffset}"); 
    il.Emit(OpCodes.Blt_S, labels[2]);
    MelonLogger.Msg($"Blt_S {il.ILOffset}"); 
    // end loop


    il.Emit(OpCodes.Ldloc_0);
    il.Emit(OpCodes.Ret);

    OneParameter<ulong, Item> del =
        (OneParameter<ulong, Item>)dynamicMethod.CreateDelegate(typeof(OneParameter<ulong, Item>));
    return del;
    // OneParameter<ulong, Item> getRankIL =
    //     (OneParameter<ulong, Item>)dynamicMethod.CreateDelegate(typeof(OneParameter<ulong, Item>));
    // return del;
}

The console log:

[14:05:20.761] [MoreQOD] MarkLabel 2 169
[14:05:20.763] [MoreQOD] MarkLabel 2 169
[14:05:20.764] [MoreQOD] Blt_S 235
[14:05:20.764] [MoreQOD] Blt_S 237
[14:05:20.765] [MoreQOD] MarkLabel 5 277
[14:05:20.766] [MoreQOD] MarkLabel 5 277
[14:05:20.767] [MoreQOD] Blt_S 328
[14:05:20.768] [MoreQOD] Blt_S 330
[14:05:20.770] [MoreQOD] [ERROR] System.InvalidProgramException: Invalid IL code in (wrapper dynamic-method) object:getRank (Death.Items.Item): IL_0148: blt.s     IL_01a9


  at (wrapper managed-to-native) System.Delegate.CreateDelegate_internal(System.Type,object,System.Reflection.MethodInfo,bool)
  at System.Delegate.CreateDelegate (System.Type type, System.Object firstArgument, System.Reflection.MethodInfo method, System.Boolean throwOnBindFailure, System.Boolean allowClosed) [0x002f0] in <9aad1b3a47484d63ba2b3985692d80e9>:0
  at System.Delegate.CreateDelegate (System.Type type, System.Object firstArgument, System.Reflection.MethodInfo method) [0x00000] in <9aad1b3a47484d63ba2b3985692d80e9>:0
  at System.Reflection.Emit.DynamicMethod.CreateDelegate (System.Type delegateType) [0x00029] in <9aad1b3a47484d63ba2b3985692d80e9>:0
  at MQOD.Sort.GenerateILGetRankIL () [0x00957] in <1efc024047cc4f579f5b6f749046551d>:0
  at MQOD.SortShop.sortShop () [0x00028] in <1efc024047cc4f579f5b6f749046551d>:0
  at MQOD.MQOD.OnLateUpdate () [0x0037a] in <1efc024047cc4f579f5b6f749046551d>:0
  at MelonLoader.MelonEvent+<>c.<Invoke>b__1_0 (MelonLoader.LemonAction x) [0x00000] in <6425afb6ac6a429aaf770255d2f7de57>:0
  at MelonLoader.MelonEventBase`1[T].Invoke (System.Action`1[T] delegateInvoker) [0x00018] in <6425afb6ac6a429aaf770255d2f7de57>:0

I have tracked the issue down to that last call of Blt_S, because we can see from the error that the offending code happens at offset IL_0148 which corresponds to 328 in decimal in the log:

...
    il.Emit(OpCodes.Blt_S, labels[2]);
...

The generated IL uses the address IL_01a9 for labels[2] but from my logging I can see that MarkLabel(labels[2]) happens at ILOffset '169' decimal or 'A9' in hex. So IL_01a9 is off by exactly 100 in hex or 256 in decimal. So the generated IL should be "IL_0148: blt.s IL_00a9" and not "IL_0148: blt.s IL_01a9", right?

@manjaroman2
Copy link
Author

manjaroman2 commented Sep 9, 2024

After some further researching I think I found the mistake in System.Reflection.Emit/ILGenerator.cs:

int diff = labels [fixups [i].label_idx].addr - (fixups [i].pos + fixups [i].offset);

Addr will be set when MarkLabel is called, which is before the Emit call that will set the .pos variable. This results in pos being bigger than addr, which results in a negative int, which is then cast to a byte resulting in this 256 offset:
https://stackoverflow.com/questions/10574799/c-the-result-of-casting-a-negative-integer-to-a-byte

The fix would simply be to reverse the order:

int diff = fixups [i].pos - labels [fixups [i].label_idx].addr + fixups [i].offset;

Edit: On second thought this would mean that it's also wrong in dotnet? https://github.com/dotnet/runtime/blob/main/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/ControlFlowBuilder.cs#L325

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant