From fb85e5a3ed22c9c34c0583c9ea7c4fdeefee223c Mon Sep 17 00:00:00 2001 From: Alex Corrado Date: Mon, 5 Dec 2011 01:10:21 -0500 Subject: [PATCH] Experimental support for initing the native vtptr ourselves (enable with -define:INIT_NATIVE_VTABLES) Doing this ourselves instead of depending on the C++ ctor to do it will allow us to instantiate classes that have vtables and do not explicitly define any constructors (thus only having the C++ implicitly-defined default constructor, which isn't exported as a symbol in the lib). --- src/Mono.Cxxi/Abi/CppAbi.cs | 46 ++++++++++- src/Mono.Cxxi/Abi/EmitInfo.cs | 2 +- src/Mono.Cxxi/Abi/Impl/ItaniumAbi.cs | 21 +++++ src/Mono.Cxxi/Abi/SymbolResolver.cs | 113 +++++++++++++++++++++++++++ src/Mono.Cxxi/CppLibrary.cs | 6 +- src/Mono.Cxxi/Makefile.am | 1 + src/Mono.Cxxi/Mono.Cxxi.csproj | 1 + 7 files changed, 184 insertions(+), 6 deletions(-) create mode 100644 src/Mono.Cxxi/Abi/SymbolResolver.cs diff --git a/src/Mono.Cxxi/Abi/CppAbi.cs b/src/Mono.Cxxi/Abi/CppAbi.cs index f6b535b..20000cd 100644 --- a/src/Mono.Cxxi/Abi/CppAbi.cs +++ b/src/Mono.Cxxi/Abi/CppAbi.cs @@ -71,6 +71,7 @@ namespace Mono.Cxxi.Abi { protected static readonly MethodInfo marshal_offsetof = typeof (Marshal).GetMethod ("OffsetOf"); protected static readonly MethodInfo marshal_structuretoptr = typeof (Marshal).GetMethod ("StructureToPtr"); protected static readonly MethodInfo marshal_ptrtostructure = typeof (Marshal).GetMethod ("PtrToStructure", BindingFlags.Static | BindingFlags.Public, null, new Type [] { typeof (IntPtr), typeof (Type) }, null); + protected static readonly MethodInfo marshal_writeintptr = typeof (Marshal).GetMethod ("WriteIntPtr", BindingFlags.Static | BindingFlags.Public, null, new Type [] { typeof (IntPtr), typeof (IntPtr) }, null); protected static readonly FieldInfo intptr_zero = typeof (IntPtr).GetField ("Zero"); // These methods might be more commonly overridden for a given C++ ABI: @@ -89,6 +90,14 @@ namespace Mono.Cxxi.Abi { return MethodType.Native; } + // Implementing this is recommended.. + // otherwise it is not possible to instantiate classes that have vtables and only implicitly defined ctor + // Return null if not implemented or if the symbol name of the vtable for the passed typeInfo cannot be mangled (i.e. because there's no vtable) + protected virtual string GetMangledVTableName (CppTypeInfo typeInfo) + { + return null; + } + // The members below must be implemented for a given C++ ABI: public abstract CallingConvention? GetCallingConvention (MethodInfo methodInfo); @@ -120,7 +129,15 @@ namespace Mono.Cxxi.Abi { DefineProperty (typeInfo, property); typeInfo.emit_info.ctor_il.Emit (OpCodes.Ret); - return (ICppClass)Activator.CreateInstance (typeInfo.emit_info.type_builder.CreateType (), typeInfo); + + var native_vtable = default (IntPtr); + +#if INIT_NATIVE_VTABLES + var vtable_symbol = GetMangledVTableName (typeInfo); + if (vtable_symbol != null) + native_vtable = SymbolResolver.ResolveSymbol (SymbolResolver.LoadImage (ref typeInfo.Library.name), vtable_symbol); +#endif + return (ICppClass)Activator.CreateInstance (typeInfo.emit_info.type_builder.CreateType (), typeInfo, native_vtable); } throw new InvalidOperationException ("This type has already been implemented"); @@ -180,9 +197,10 @@ namespace Mono.Cxxi.Abi { impl_type.AddInterfaceImplementation (typeInfo.InterfaceType); var typeinfo_field = impl_type.DefineField ("_typeInfo", typeof (CppTypeInfo), FieldAttributes.InitOnly | FieldAttributes.Private); + var native_vtable_field = impl_type.DefineField ("_nativeVTable", typeof (IntPtr), FieldAttributes.InitOnly | FieldAttributes.Private); ConstructorBuilder ctor = impl_type.DefineConstructor (MethodAttributes.Public, CallingConventions.Standard, - new Type[] { typeof (CppTypeInfo) }); + new Type[] { typeof (CppTypeInfo), typeof (IntPtr) }); var ctor_il = ctor.GetILGenerator (); @@ -191,8 +209,14 @@ namespace Mono.Cxxi.Abi { ctor_il.Emit (OpCodes.Ldarg_1); ctor_il.Emit (OpCodes.Stfld, typeinfo_field); + // this._nativeVTable = (vtable ptr passed to constructor) + ctor_il.Emit (OpCodes.Ldarg_0); + ctor_il.Emit (OpCodes.Ldarg_2); + ctor_il.Emit (OpCodes.Stfld, native_vtable_field); + typeInfo.emit_info.ctor_il = ctor_il; typeInfo.emit_info.typeinfo_field = typeinfo_field; + typeInfo.emit_info.native_vtable_field = native_vtable_field; typeInfo.emit_info.type_builder = impl_type; } @@ -479,8 +503,8 @@ namespace Mono.Cxxi.Abi { protected virtual void EmitManagedAlloc (CppTypeInfo typeInfo, MethodInfo interfaceMethod) { var il = typeInfo.emit_info.current_il; + var cppip = il.DeclareLocal (typeof (CppInstancePtr)); - // this._typeInfo.NativeSize il.Emit (OpCodes.Ldarg_0); il.Emit (OpCodes.Ldfld, typeInfo.emit_info.typeinfo_field); @@ -492,6 +516,22 @@ namespace Mono.Cxxi.Abi { il.Emit (OpCodes.Callvirt, typeinfo_nativesize); il.Emit (OpCodes.Newobj, cppip_fromsize); } + il.Emit (OpCodes.Stloc, cppip); + + var unknown_native_vtable = il.DefineLabel (); + il.Emit (OpCodes.Ldarg_0); + il.Emit (OpCodes.Ldfld, typeInfo.emit_info.native_vtable_field); + il.Emit (OpCodes.Brfalse_S, unknown_native_vtable); + + il.Emit (OpCodes.Ldloca, cppip); + il.Emit (OpCodes.Call, cppip_native); + + il.Emit (OpCodes.Ldarg_0); + il.Emit (OpCodes.Ldfld, typeInfo.emit_info.native_vtable_field); + il.Emit (OpCodes.Call, marshal_writeintptr); + + il.MarkLabel (unknown_native_vtable); + il.Emit (OpCodes.Ldloc, cppip); } protected virtual void EmitConstruct (CppTypeInfo typeInfo, MethodInfo nativeMethod, PInvokeSignature psig, diff --git a/src/Mono.Cxxi/Abi/EmitInfo.cs b/src/Mono.Cxxi/Abi/EmitInfo.cs index 3e3cda9..6b12ce4 100644 --- a/src/Mono.Cxxi/Abi/EmitInfo.cs +++ b/src/Mono.Cxxi/Abi/EmitInfo.cs @@ -5,7 +5,7 @@ namespace Mono.Cxxi.Abi { public class EmitInfo { public TypeBuilder type_builder; - public FieldBuilder typeinfo_field; + public FieldBuilder typeinfo_field, native_vtable_field; public ILGenerator ctor_il, current_il; } } diff --git a/src/Mono.Cxxi/Abi/Impl/ItaniumAbi.cs b/src/Mono.Cxxi/Abi/Impl/ItaniumAbi.cs index a442aee..aab6168 100644 --- a/src/Mono.Cxxi/Abi/Impl/ItaniumAbi.cs +++ b/src/Mono.Cxxi/Abi/Impl/ItaniumAbi.cs @@ -6,6 +6,7 @@ // Andreia Gaita (shana@spoiledcat.net) // // Copyright (C) 2010-2011 Alexander Corrado +// Copyright 2011 Xamarin Inc (http://www.xamarin.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the @@ -197,6 +198,26 @@ namespace Mono.Cxxi.Abi { return code.ToString (); } + protected override string GetMangledVTableName (CppTypeInfo typeInfo) + { + var compressMap = new Dictionary (); + var type = typeInfo.GetMangleType (); + var nm = new StringBuilder ("_ZTV", 30); + + if (type.Namespaces != null) { + nm.Append ('N'); + foreach (var ns in type.Namespaces) + nm.Append (GetIdentifier (compressMap, ns)); + } + + nm.Append (GetIdentifier (compressMap, type.ElementTypeName)); + + if (type.Namespaces != null) + nm.Append ('E'); + + return nm.ToString (); + } + string GetIdentifier (Dictionary compressMap, string identifier) { int cid; diff --git a/src/Mono.Cxxi/Abi/SymbolResolver.cs b/src/Mono.Cxxi/Abi/SymbolResolver.cs new file mode 100644 index 0000000..6688010 --- /dev/null +++ b/src/Mono.Cxxi/Abi/SymbolResolver.cs @@ -0,0 +1,113 @@ +// +// Mono.Cxxi.Abi.SymbolResolver.cs: Platform-independent dynamic symbol lookup +// +// Author: +// Alexander Corrado (alexander.corrado@gmail.com) +// +// Copyright 2011 Xamarin Inc (http://www.xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +using System; +using System.Runtime.InteropServices; + +namespace Mono.Cxxi.Abi { + internal static class SymbolResolver { + + static readonly string [] formats; + static readonly Func load_image; + static readonly Func resolve_symbol; + + static SymbolResolver () + { + switch (Environment.OSVersion.Platform) { + + case PlatformID.Unix: + case PlatformID.MacOSX: + load_image = dlopen; + resolve_symbol = dlsym; + formats = new[] { + "{0}", + "{0}.so", + "{0}.dylib", + "lib{0}.so", + "lib{0}.dylib", + "{0}.bundle" + }; + break; + + default: + load_image = LoadLibrary; + resolve_symbol = GetProcAddress; + formats = new[] { "{0}", "{0}.dll" }; + break; + } + } + + // will fix up name with a more precise name to speed up later p/invokes (hopefully?) + public static IntPtr LoadImage (ref string name) + { + foreach (var format in formats) { + var attempted = string.Format (format, name); + var ptr = load_image (attempted); + if (ptr != IntPtr.Zero) { + name = attempted; + return ptr; + } + } + return IntPtr.Zero; + } + + public static IntPtr ResolveSymbol (IntPtr image, string symbol) + { + if (image != IntPtr.Zero) + return resolve_symbol (image, symbol); + return IntPtr.Zero; + } + + #region POSIX + + static IntPtr dlopen (string path) + { + return dlopen (path, 0x0); + } + + [DllImport ("dl", CharSet=CharSet.Ansi)] + static extern IntPtr dlopen (string path, int flags); + + [DllImport ("dl", CharSet=CharSet.Ansi)] + static extern IntPtr dlsym (IntPtr handle, string symbol); + + #endregion + + #region Win32 + + [DllImport("kernel32", SetLastError=true)] + static extern IntPtr LoadLibrary (string lpFileName); + + [DllImport("kernel32", CharSet=CharSet.Ansi, ExactSpelling=true, SetLastError=true)] + static extern IntPtr GetProcAddress (IntPtr hModule, string procName); + + #endregion + + } +} + diff --git a/src/Mono.Cxxi/CppLibrary.cs b/src/Mono.Cxxi/CppLibrary.cs index ff9db81..7b64810 100644 --- a/src/Mono.Cxxi/CppLibrary.cs +++ b/src/Mono.Cxxi/CppLibrary.cs @@ -59,9 +59,11 @@ namespace Mono.Cxxi { internal static ModuleBuilder interopModule; public CppAbi Abi { get; private set; } - public string Name { get; private set; } public InlineMethods InlineMethodPolicy { get; private set; } + internal string name; + public string Name { get { return name; } } + static CppLibrary () { AssemblyName assemblyName = new AssemblyName ("__CppLibraryImplAssembly"); @@ -89,7 +91,7 @@ namespace Mono.Cxxi { if (abi == null) throw new ArgumentNullException ("Abi cannot be NULL."); - this.Name = name; + this.name = name; this.Abi = abi; this.InlineMethodPolicy = inlinePolicy; } diff --git a/src/Mono.Cxxi/Makefile.am b/src/Mono.Cxxi/Makefile.am index 745859b..d343e0d 100644 --- a/src/Mono.Cxxi/Makefile.am +++ b/src/Mono.Cxxi/Makefile.am @@ -49,6 +49,7 @@ FILES = \ Abi/Impl/ItaniumTypeInfo.cs \ Abi/Impl/MsvcAbi.cs \ Abi/MethodType.cs \ + Abi/SymbolResolver.cs \ Abi/VTable.cs \ AssemblyInfo.cs \ Attributes.cs \ diff --git a/src/Mono.Cxxi/Mono.Cxxi.csproj b/src/Mono.Cxxi/Mono.Cxxi.csproj index 721c3f8..2bd138c 100644 --- a/src/Mono.Cxxi/Mono.Cxxi.csproj +++ b/src/Mono.Cxxi/Mono.Cxxi.csproj @@ -78,6 +78,7 @@ +