Giter Site home page Giter Site logo

Comments (2)

3F avatar 3F commented on May 13, 2024

Good news! I noticed the following interesting thing. Final logic from implemented managed EmitCalli() and its calling convention 'Standard' is much closely related to actual logic that's required for our unmanaged type :)

Decompiled versions below was just for my convenience when debugging at this case. But the original code you can find in original coreclr repo or from my mod repo for .NET DllExport project.
See src part for System.Private.CoreLib

So! What I found:

All code below has been trimmed (+added specific parts to this case) just to illustrate the main flow between different implementations.

Unmanaged EmitCalli ~

...
ModuleBuilder moduleBuilder = (ModuleBuilder)m_methodBuilder.Module;
...
SignatureHelper methodSigHelper = SignatureHelper.GetMethodSigHelper(moduleBuilder, unmanagedCallConv, returnType);
if (parameterTypes != null)
{
    for (int i = 0; i < num2; i++)
    {
        methodSigHelper.AddArgument(parameterTypes[i]);
    }
}
if (returnType != typeof(void))
{
    num++;
}
if (parameterTypes != null)
{
    num -= num2; // num2 = parameterTypes.Length;
}
num--;
UpdateStackSize(OpCodes.Calli, num);
EnsureCapacity(7);
Emit(OpCodes.Calli);
RecordTokenFixup();
PutInteger4(moduleBuilder.GetSignatureToken(methodSigHelper).Token);

Managed EmitCalli when 'Standard' ~

...
ModuleBuilder moduleBuilder = (ModuleBuilder)m_methodBuilder.Module;
SignatureHelper memberRefSignature = GetMemberRefSignature(callingConvention, returnType, parameterTypes, optionalParameterTypes);
EnsureCapacity(7);
Emit(OpCodes.Calli);
if (returnType != typeof(void))
{
    num++;
}
if (parameterTypes != null)
{
    num -= parameterTypes.Length;
}
...
num--;
UpdateStackSize(OpCodes.Calli, num);
RecordTokenFixup();
PutInteger4(moduleBuilder.GetSignatureToken(memberRefSignature).Token);

// >> GetMemberRefSignature
    return GetMemberRefSignature(call, returnType, parameterTypes, optionalParameterTypes, 0);
    // >> GetMemberRefSignature
        return ((ModuleBuilder)m_methodBuilder.Module).GetMemberRefSignature(call, returnType, parameterTypes, optionalParameterTypes, cGenericParameters);
        // >> GetMemberRefSignature
        
            SignatureHelper methodSigHelper = SignatureHelper.GetMethodSigHelper(this, call, returnType, cGenericParameters);
            if (parameterTypes != null)
            {
                foreach (Type clsArgument in parameterTypes)
                {
                    methodSigHelper.AddArgument(clsArgument);
                }
            }
            ...
            return methodSigHelper;

Did you notice it? All minimal instructions are the same to unmanaged implementation as we can see here.

Good! We only need to inspect method signature!

Method signature and internal GetMethodSigHelper

Both implementations above uses internal MdSigCallingConvention:

CallConvMask = 0xF,
Default = 0x0,
C = 0x1,
StdCall = 0x2,
ThisCall = 0x3,
FastCall = 0x4,
Vararg = 0x5,
Field = 0x6,
LocalSig = 0x7,
Property = 0x8,
Unmgd = 0x9,
GenericInst = 0xA,
Generic = 0x10,
HasThis = 0x20,
ExplicitThis = 0x40

After some different layers they have the final m_signature processing as follow:

if (m_currSig + 4 > m_signature.Length)
{
    m_signature = ExpandArray(m_signature);
}
if (data <= 127)
{
    m_signature[m_currSig++] = (byte)(data & 0xFF);
    return;
}
if (data <= 16383)
{
    m_signature[m_currSig++] = (byte)((data >> 8) | 0x80);
    m_signature[m_currSig++] = (byte)(data & 0xFF);
    return;
}
if (data <= 536870911)
{
    m_signature[m_currSig++] = (byte)((data >> 24) | 0xC0);
    m_signature[m_currSig++] = (byte)((data >> 16) & 0xFF);
    m_signature[m_currSig++] = (byte)((data >> 8) & 0xFF);
    m_signature[m_currSig++] = (byte)(data & 0xFF);
    return;
}

And this is it, an added m_signature item above (means when MdSigCallingConvention data) will be between 1 - 4 (inclusive) for unmanaged, and 0 for managed ('Standard' should be processed as MdSigCallingConvention.Default).

That is, for example, we can try to control it like:

0 1 1 8 0 ... -> 2 1 1 8 0 ...

I'll update PR soon because I already have a working draft :p

from conari.

3F avatar 3F commented on May 13, 2024

Cross-link to a project that provides an independent Unmanaged EmitCalli implementation
- https://github.com/3F/UnmanagedEmitCalli

from conari.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.