C#: Extract function pointer types from CIL

This commit is contained in:
Tamas Vajk
2020-12-16 14:12:25 +01:00
parent 5fa0dd719c
commit 0c213d0926
13 changed files with 228 additions and 12 deletions

View File

@@ -8,6 +8,7 @@ namespace Semmle.Extraction.CIL.Entities
ValueOrRefType,
TypeParameter,
Array,
Pointer
Pointer,
FunctionPointer
}
}

View File

@@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection.Metadata;
namespace Semmle.Extraction.CIL.Entities
{
internal sealed class FunctionPointerType : Type, IParameterizable, ICustomModifierReceiver
{
private readonly MethodSignature<Type> signature;
public FunctionPointerType(Context cx, MethodSignature<Type> signature) : base(cx)
{
this.signature = signature;
}
public override CilTypeKind Kind => CilTypeKind.FunctionPointer;
public override string Name
{
get
{
using var id = new StringWriter();
WriteName(
id.Write,
t => id.Write(t.Name),
signature
);
return id.ToString();
}
}
public override Namespace? ContainingNamespace => Cx.GlobalNamespace;
public override Type? ContainingType => null;
public override int ThisTypeParameterCount => throw new System.NotImplementedException();
public override IEnumerable<Type> TypeParameters => throw new System.NotImplementedException();
public override Type Construct(IEnumerable<Type> typeArguments) => throw new System.NotImplementedException();
public override void WriteAssemblyPrefix(TextWriter trapFile) { }
public override void WriteId(TextWriter trapFile, bool inContext)
{
WriteName(
trapFile.Write,
t => t.WriteId(trapFile, inContext),
signature
);
}
internal static void WriteName<TType>(Action<string> write, Action<TType> writeType, MethodSignature<TType> signature)
{
write("delegate* ");
write(GetCallingConvention(signature.Header.CallingConvention));
write("<");
foreach (var pt in signature.ParameterTypes)
{
writeType(pt);
write(",");
}
writeType(signature.ReturnType);
write(">");
}
internal static string GetCallingConvention(SignatureCallingConvention callingConvention)
{
if (callingConvention == SignatureCallingConvention.Default)
{
return "managed";
}
if (callingConvention == SignatureCallingConvention.Unmanaged)
{
return "unmanaged";
}
return $"unmanaged[{callingConvention}]";
}
public override IEnumerable<IExtractionProduct> Contents
{
get
{
foreach (var c in base.Contents)
{
yield return c;
}
var retType = signature.ReturnType;
if (retType is ModifiedType mt)
{
retType = mt.Unmodified;
yield return Tuples.cil_custom_modifiers(this, mt.Modifier, mt.IsRequired);
}
yield return Tuples.cil_function_pointer_return_type(this, retType);
yield return Tuples.cil_function_pointer_calling_conventions(this, signature.Header.CallingConvention);
var i = 0;
foreach (var p in signature.ParameterTypes)
{
var t = p;
if (t is ModifiedType mtparam)
{
t = mtparam.Unmodified;
yield return Tuples.cil_custom_modifiers(this, mtparam.Modifier, mtparam.IsRequired);
}
yield return Cx.Populate(new Parameter(Cx, this, i++, t));
}
}
}
}
}

View File

@@ -0,0 +1,7 @@
namespace Semmle.Extraction.CIL.Entities
{
internal interface IParameterizable : IEntity
{
}
}

View File

@@ -9,7 +9,7 @@ namespace Semmle.Extraction.CIL.Entities
/// <summary>
/// A method entity.
/// </summary>
internal abstract class Method : TypeContainer, IMember, ICustomModifierReceiver
internal abstract class Method : TypeContainer, IMember, ICustomModifierReceiver, IParameterizable
{
protected MethodTypeParameter[]? genericParams;
protected GenericContext gc;

View File

@@ -37,6 +37,13 @@ namespace Semmle.Extraction.CIL.Entities
public override void WriteAssemblyPrefix(TextWriter trapFile) => throw new NotImplementedException();
public override void WriteId(TextWriter trapFile, bool inContext) => throw new NotImplementedException();
public override void WriteId(TextWriter trapFile, bool inContext)
{
Unmodified.WriteId(trapFile, inContext);
trapFile.Write(IsRequired ? " modreq" : " modopt");
trapFile.Write("(");
Modifier.WriteId(trapFile, inContext);
trapFile.Write(")");
}
}
}

View File

@@ -8,11 +8,11 @@ namespace Semmle.Extraction.CIL.Entities
/// </summary>
internal sealed class Parameter : LabelledEntity
{
private readonly Method method;
private readonly IParameterizable method;
private readonly int index;
private readonly Type type;
public Parameter(Context cx, Method m, int i, Type t) : base(cx)
public Parameter(Context cx, IParameterizable m, int i, Type t) : base(cx)
{
method = m;
index = i;

View File

@@ -48,10 +48,20 @@ namespace Semmle.Extraction.CIL.Entities
private struct FnPtr : ITypeSignature
{
private readonly MethodSignature<ITypeSignature> signature;
public FnPtr(MethodSignature<ITypeSignature> signature)
{
this.signature = signature;
}
public void WriteId(TextWriter trapFile, GenericContext gc)
{
trapFile.Write("<method signature>");
FunctionPointerType.WriteName(
trapFile.Write,
t => t.WriteId(trapFile, gc),
signature
);
}
}
@@ -62,7 +72,7 @@ namespace Semmle.Extraction.CIL.Entities
new ByRef(elementType);
ITypeSignature ISignatureTypeProvider<ITypeSignature, object>.GetFunctionPointerType(MethodSignature<ITypeSignature> signature) =>
new FnPtr();
new FnPtr(signature);
private class Instantiation : ITypeSignature
{

View File

@@ -23,7 +23,7 @@ namespace Semmle.Extraction.CIL.Entities
elementType; // ??
Type ISignatureTypeProvider<Type, GenericContext>.GetFunctionPointerType(MethodSignature<Type> signature) =>
cx.ErrorType; // Don't know what to do !!
cx.Populate(new FunctionPointerType(cx, signature));
Type IConstructedTypeProvider<Type>.GetGenericInstantiation(Type genericType, ImmutableArray<Type> typeArguments) =>
genericType.Construct(typeArguments);

View File

@@ -86,6 +86,12 @@ namespace Semmle.Extraction.CIL
internal static Tuple cil_method(Method method, string name, Type declType, Type returnType) =>
new Tuple("cil_method", method, name, declType, returnType);
internal static Tuple cil_function_pointer_return_type(FunctionPointerType fnptr, Type returnType) =>
new Tuple("cil_function_pointer_return_type", fnptr, returnType);
internal static Tuple cil_function_pointer_calling_conventions(FunctionPointerType fnptr, System.Reflection.Metadata.SignatureCallingConvention callingConvention) =>
new Tuple("cil_function_pointer_calling_conventions", fnptr, (int)callingConvention);
internal static Tuple cil_method_implementation(MethodImplementation impl, Method method, Assembly assembly) =>
new Tuple("cil_method_implementation", impl, method, assembly);
@@ -101,7 +107,7 @@ namespace Semmle.Extraction.CIL
internal static Tuple cil_newslot(Method method) =>
new Tuple("cil_newslot", method);
internal static Tuple cil_parameter(Parameter p, Method m, int i, Type t) =>
internal static Tuple cil_parameter(Parameter p, IParameterizable m, int i, Type t) =>
new Tuple("cil_parameter", p, m, i, t);
internal static Tuple cil_parameter_in(Parameter p) =>

View File

@@ -1695,6 +1695,7 @@ case @cil_type.kind of
| 1 = @cil_typeparameter
| 2 = @cil_array_type
| 3 = @cil_pointer_type
| 4 = @cil_function_pointer_type
;
cil_type(
@@ -1713,6 +1714,10 @@ cil_array_type(
int element_type: @cil_type ref,
int rank: int ref);
cil_function_pointer_return_type(
unique int id: @cil_function_pointer_type ref,
int return_type: @cil_type ref);
cil_method(
unique int id: @cil_method,
string name: string ref,
@@ -1746,12 +1751,13 @@ cil_field(
@cil_variable = @cil_field | @cil_stack_variable;
@cil_stack_variable = @cil_local_variable | @cil_parameter;
@cil_member = @cil_method | @cil_type | @cil_field | @cil_property | @cil_event;
@cil_custom_modifier_receiver = @cil_method | @cil_property | @cil_parameter | @cil_field; // todo: add function pointer type
@cil_custom_modifier_receiver = @cil_method | @cil_property | @cil_parameter | @cil_field | @cil_function_pointer_type;
@cil_parameterizable = @cil_method | @cil_function_pointer_type;
#keyset[method, index]
#keyset[parameterizable, index]
cil_parameter(
unique int id: @cil_parameter,
int method: @cil_method ref,
int parameterizable: @cil_parameterizable ref,
int index: int ref,
int param_type: @cil_type ref);
@@ -1796,6 +1802,10 @@ cil_local_variable(
int index: int ref,
int var_type: @cil_type ref);
cil_function_pointer_calling_conventions(
int id: @cil_function_pointer_type ref,
int kind: int ref);
// CIL handlers (exception handlers etc).
case @cil_handler.kind of

View File

@@ -0,0 +1,52 @@
using System;
#nullable enable
public class FnPointer
{
public unsafe static class Program
{
static delegate*<int> pointer = &M0;
public static int M0()
{
return 0;
}
static void M1(delegate*<ref int, out object?, int> f)
{
int i = 42;
int j = f(ref i, out object? o);
}
static void M2<T>(delegate* unmanaged[Stdcall/*, StdcallSuppressGCTransition*/]<ref int, out object?, T, void> f) where T : new()
{
int i = 42;
f(ref i, out object? o, new T());
}
static void M3(delegate* managed<ref int, out object?, in int, ref int> f)
{
int i = 42;
ref int j = ref f(ref i, out object? o, in i);
}
static void M4<T>(delegate*<T, int> f) where T : new()
{
int j = f(new T());
}
static void M5(delegate*<B, A> f, delegate*<A, B> ff)
{
M5(ff, ff); // implicit conversion due to implicit reference conversion
}
static void M6(delegate*<int*, void*> f, delegate*<void*, int*> ff)
{
M6(ff, ff); // implicit conversion due to implicit pointer conversion
}
class A { }
class B : A { }
}
}

View File

@@ -0,0 +1,7 @@
// semmle-extractor-options: --cil
using System;
class Test
{
}