🌐 AI搜索 & 代理 主页
Skip to content

Conversation

@ghorsington
Copy link
Contributor

@ghorsington ghorsington commented Jul 31, 2022

On x86, assume the following method signature:

class Foo {
  long Method(int a, int b) {}
}

In other words, the method returns a primitive value type, in which case there is no
__thiscall return value pointer. However, the __thiscall return wrapper was still generated because it passes the current wrapper generator check:

if (from is MethodInfo fromInfo && !from.IsStatic &&
to is MethodInfo toInfo && to.IsStatic &&
fromInfo.ReturnType == toInfo.ReturnType &&
fromInfo.ReturnType.IsValueType &&
(glueThiscallStructRetPtr =
from.DeclaringType?.IsValueType ?? false && from.GetParameters().Length == 0 ? GlueThiscallInStructRetPtr :
GlueThiscallStructRetPtr
) != GlueThiscallStructRetPtrOrder.Original) {
int size = fromInfo.ReturnType.GetManagedSize();
// This assumes that 8 bytes long structs work fine in 64-bit envs but not 32-bit envs.
if (size == 3 || size == 5 || size == 6 || size == 7 || size > IntPtr.Size) {

Specifically:

  • fromInfo.ReturnType.IsValueType == typeof(long).IsValueType == true -> passes the first if
  • sizeof(long) > IntPtr.Size $\Leftrightarrow$ 8 > 4 $\Leftrightarrow$ true (because we're running x86, specifically 32-bit) -> passes second if

This causes argument shifting, observed at least in Unity 2018 and .NET 6.

This commit fixes the logic by modifying IsValueType => IsValueType && !IsPrimitive && !IsEnum which prevents
non-struct return values getting __thiscall return wrappers. The logic is similar to Harmony 2:s return buffer check:

https://github.com/pardeike/Harmony/blob/d1aa13b919adf1089ec8b3198c5c081873c51440/Harmony/Internal/StructReturnBufferCheck.cs#L117

This PR was tested on UnityMono (MonoBleedingEdge) and CoreCLR (.NET 5 & 6). For the latter, a Unit test was written (MonoMod/MonoMod#99) and run locally on 32bit CoreCLR

On x86, assume the following method signature:

class Foo {
  long Method(int a, int b) {}
}

In other words, the method returns a *primitive* value type, in which case there is no
__thiscall return value pointer. However, the __thiscall return wrapper was still generated because
method.ReturnType.IsValueType == typeof(long).IsValueType == true even though long is not a struct type.

This commit fixes this by modifying IsValueType => IsValueType && !IsPrimitive && !IsEnum which prevents
non-struct return values getting __thiscall return wrappers.
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

Successfully merging this pull request may close these issues.

2 participants