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

CallSite with "platformapi" calling convention #145

Closed
thefiddler opened this issue Nov 29, 2013 · 9 comments
Closed

CallSite with "platformapi" calling convention #145

thefiddler opened this issue Nov 29, 2013 · 9 comments

Comments

@thefiddler
Copy link

According to the ECMA-335 spec, page 252, there exists a calling convention called "Platformapi" with a value of 0x0100. According to the description on page 188, the runtime translates this to an appropriate calling convention for the underlying platform (typically stdcall on Windows and cdecl on other platforms.)

When using System.Reflection.Emit, you can emit calli with an unmanaged callsite using the platformapi calling convention:

ILGenerator il;
il.Emit(OpCodes.Calli, CallingConvention.Winapi, typeof(void), new Type[] { typeof(int) });

However, when trying to emit a calli through Cecil, there is no way to specify the platformapi convention (see MethodCallingConvention).

Is there some other way to construct a platformapi callsite? If not, would it be possible to add this to Cecil?

@jbevain
Copy link
Owner

jbevain commented Nov 29, 2013

We could certainly add it to the API, in the meantime you can simply pass 0x0100 and cast it to MethodCallingConvetion :)

@thefiddler
Copy link
Author

The values don't appear to match (e.g. MethodCallingConvention.C is 0x1 in Cecil, but is defined as 0x0200 in the spec.) If it's just a matter of finding where the corresponding byte is emitted, I could probably cook up a patch.

Edit: huh, that's weird, this value appears to be written out as-is in AssemblyWriter.WriteMethodSignature(). I may be misinterpreting something in the spec: it says that these values are specific to pinvokeimpl (but SRE allows you to emit "winapi" which appears to work identically to "platformapi" for calli callsites.)

Any ideas?

@jbevain
Copy link
Owner

jbevain commented Nov 29, 2013

Ah I see. You're mixing the pinvoke attributes and the byte flags used for the calling convention in a method signature.

For the method signature, they're read in straight from the byte array:

https://github.com/jbevain/cecil/blob/master/Mono.Cecil/AssemblyReader.cs#L2860

and written as is:

https://github.com/jbevain/cecil/blob/master/Mono.Cecil/AssemblyWriter.cs#L2019

For the pinvoke attributes they're in:

https://github.com/jbevain/cecil/blob/master/Mono.Cecil/PInvokeAttributes.cs

And serialized properly as well.

@thefiddler
Copy link
Author

Aha, makes sense.

In that case, what does SRE EmitCalli do? The documentation explicitly lists Winapi as supported, and it appears to work when you use it both on .Net and Mono.

Edit (link markup failure):
http://msdn.microsoft.com/en-us/library/d81ee808(v=vs.110).aspx
http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.callingconvention(v=vs.110).aspx

@jbevain
Copy link
Owner

jbevain commented Nov 29, 2013

In mono it ends up doing:

https://github.com/mono/mono/blob/master/mono/metadata/reflection.c#L3243

So the calling convention byte is set to the unmanaged calling convention minus one.

https://github.com/mono/mono/blob/master/mcs/class/corlib/System.Runtime.InteropServices/CallingConvention.cs

WinAPI is 1, so I guess passing a zero value should do the trick, which is convenient as it's MethodCallingConvention.Default :)

@thefiddler
Copy link
Author

If I pass MethodCallingConvention.Default, then I get runtime failures on .Net/Windows. This affects mainly string/StringBuilder parameters (I marshal everything else myself), so I guess Default does not indicate that the callsite is unmanaged and the runtime does not generate the necessary unmanaged thunking code.

Is there any way to verify whether a callsite is unmanaged? (If I open the binary in Xamarin Studio I do not see any mention of "unmanaged" in the dissassembly.)

Edit: this is for an open-source projects btw (OpenTK)

Edit 2: If I use MethodCallingConvention.StdCall, everything appears to be more or less working on .Net/Windows, so there's definitely something fishy going on with Default.

@jbevain
Copy link
Owner

jbevain commented Nov 29, 2013

It should be pretty easy to see what's going on with ildasm.

In ILDASM you can set the Show Bytes option. So you can get the token of the signature that is passed to calli. Then you can do CTRL+M in ildasm, browse to the signatures (the easiest way is to search for Signature #1), and compare what is emitted by SRE with what's emitted with Cecil.

@thefiddler
Copy link
Author

Ok, just tried that. Results below.

System.Reflection.Emit

// il.EmitCalli(OpCodes.Calli, CallingConvention.Winapi, typeof(void), new Type[] { typeof(int) });

// On .Net/Windows
.method public static void  ArrayElement(int32 A_0) cil managed
{
  // Code size       18 (0x12)
  .maxstack  3
  IL_0000:  ldarg.0
  IL_0001:  ldsfld     native int OpenTK.InteropHelper::EntryPoints$PST04000001
  IL_0006:  ldc.i4.s   18
  IL_0008:  nop
  IL_0009:  nop
  IL_000a:  nop
  IL_000b:  ldelem.i
  IL_000c:  calli      unmanaged stdcall void(int32)
  IL_0011:  ret
} // end of method InteropHelper::ArrayElement

CallingConvention.Winapi gives "unmanaged stdcall"!

Same thing on Mono / Windows:

// il.EmitCalli(OpCodes.Calli, CallingConvention.Winapi, typeof(void), new Type[] { typeof(int) });
.method public static void  ArrayElement(int32 A_0) cil managed
{
  // Code size       18 (0x12)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  ldsfld     native int OpenTK.InteropHelper::EntryPoints$PST04000001
  IL_0006:  ldc.i4.s   18
  IL_0008:  nop
  IL_0009:  nop
  IL_000a:  nop
  IL_000b:  ldelem.i
  IL_000c:  calli      void(int32)
  IL_0011:  ret
} // end of method InteropHelper::ArrayElement

Mono / Windows gives managed call (probably a bug in the SRE implementation of Mono.)

Next up is Mono.Cecil on .Net / Windows:

// var signature = new CallSite(module.Import(typeof(void))) { CallingConvention = MethodCallingConvention.Default, };
// signature.Parameters.Add(module.Import(typeof(int)));
// il.Emit(OpCodes.Calli, signature);
.method public hidebysig static void  ArrayElement(int32 i) cil managed
{
  // Code size       15 (0xf)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  ldsfld     native int[] OpenTK.Graphics.OpenGL.GL::EntryPoints
  IL_0006:  ldc.i4.s   18
  IL_0008:  ldelem.i
  IL_0009:  calli      void(int32)
  IL_000e:  ret
} // end of method GL::ArrayElement

MethodCallingConvention.Default gives a regular managed call.

If I switch Cecil to MethodCallingConvention.Stdcall, I get:

  IL_0009:  calli      unmanaged stdcall void(int32)

which is the same result as SRE on .Net / Windows.

Phew, so it appears that SRE "Winapi" is interpreted as an alias to "Stdcall" and what happens next is up to the runtime. No magic after all and there really isn't anything to do in Cecil (other than possibly adding MethodCallingConvention.Winapi as an alias to Stdcall.)

Thank you for your time!

@jbevain
Copy link
Owner

jbevain commented Nov 29, 2013

🙇

clamp03 pushed a commit to clamp03/cecil that referenced this issue Apr 9, 2024
jbevain#145)

* Update dependencies from https://github.com/dotnet/source-build-reference-packages build 20240109.3

Microsoft.SourceBuild.Intermediate.source-build-reference-packages
 From Version 9.0.0-alpha.1.23627.2 -> To Version 9.0.0-alpha.1.24059.3

* Update dependencies from https://github.com/dotnet/source-build-reference-packages build 20240117.1

Microsoft.SourceBuild.Intermediate.source-build-reference-packages
 From Version 9.0.0-alpha.1.23627.2 -> To Version 9.0.0-alpha.1.24067.1

* Update dependencies from https://github.com/dotnet/source-build-reference-packages build 20240125.1

Microsoft.SourceBuild.Intermediate.source-build-reference-packages
 From Version 9.0.0-alpha.1.23627.2 -> To Version 9.0.0-alpha.1.24075.1

---------

Co-authored-by: dotnet-maestro[bot] <dotnet-maestro[bot]@users.noreply.github.com>
Co-authored-by: Marek Safar <marek.safar@gmail.com>
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

2 participants