Browse Source

Fix patching of virtual tables for MSVC with RTTI

Fixes https://github.com/mono/CppSharp/issues/1151.

Signed-off-by: Dimitar Dobrev <dpldobrev@protonmail.com>
pull/1384/head
Dimitar Dobrev 6 years ago
parent
commit
4d8ab83328
  1. 3
      src/Generator/AST/VTables.cs
  2. 54
      src/Generator/Generators/CSharp/CSharpSources.cs
  3. 4
      tests/VTables/VTables.Tests.cs
  4. 6
      tests/VTables/VTables.cpp
  5. 7
      tests/VTables/VTables.cs
  6. 1
      tests/VTables/VTables.h
  7. 5
      tests/VTables/premake4.lua

3
src/Generator/AST/VTables.cs

@ -62,7 +62,8 @@ namespace CppSharp.AST @@ -62,7 +62,8 @@ namespace CppSharp.AST
{
case CppAbi.Microsoft:
return (from table in @class.Layout.VFTables
let j = table.Layout.Components.FindIndex(m => m.Method == function)
let j = table.Layout.Components.FindIndex(m => m.Method == function) -
(table.Layout.Components.Any(c => c.Kind == VTableComponentKind.RTTI) ? 1 : 0)
where j >= 0
select j).First();
default:

54
src/Generator/Generators/CSharp/CSharpSources.cs

@ -1639,15 +1639,11 @@ namespace CppSharp.Generators.CSharp @@ -1639,15 +1639,11 @@ namespace CppSharp.Generators.CSharp
for (int i = 0; i < @class.Layout.VFTables.Count; i++)
{
VFTableInfo vftable = @class.Layout.VFTables[i];
int size = vftable.Layout.Components.Count;
string vfptr = $"vfptr{(destructorOnly ? "_dtor" : string.Empty)}{i}";
WriteLine($@"var {vfptr} = Marshal.AllocHGlobal({size} * {
Context.TargetInfo.PointerWidth / 8});");
WriteLine($"__handleManagedVTables.Add(new global::CppSharp.Runtime.SafeUnmanagedMemoryHandle({vfptr}, true));");
WriteLine($"{managedVTables}[{i}] = {vfptr}.ToPointer();");
AllocateNewVTableEntries(vftable.Layout.Components, wrappedEntries,
@class.Layout.VTablePointers[i].Offset, i, destructorOnly);
@class.Layout.VTablePointers[i].Offset, i,
vftable.Layout.Components.Any(c => c.Kind == VTableComponentKind.RTTI) ? 1 : 0,
destructorOnly);
}
}
@ -1657,33 +1653,39 @@ namespace CppSharp.Generators.CSharp @@ -1657,33 +1653,39 @@ namespace CppSharp.Generators.CSharp
var managedVTables = destructorOnly ? "__ManagedVTablesDtorOnly" : "__ManagedVTables";
WriteLine($"{managedVTables} = new void*[1];");
string suffix = destructorOnly ? "_dtor" : string.Empty;
int size = @class.Layout.Layout.Components.Count;
uint pointerSize = Context.TargetInfo.PointerWidth / 8;
WriteLine($"var vtptr{suffix} = Marshal.AllocHGlobal({size} * {pointerSize});");
WriteLine($"__handleManagedVTables.Add(new global::CppSharp.Runtime.SafeUnmanagedMemoryHandle(vtptr{suffix}, true));");
WriteLine($@"var vfptr{suffix}0 = vtptr{suffix} + {
VTables.ItaniumOffsetToTopAndRTTI} * {pointerSize};");
WriteLine($"{managedVTables}[0] = vfptr{suffix}0.ToPointer();");
AllocateNewVTableEntries(@class.Layout.Layout.Components,
wrappedEntries, @class.Layout.VTablePointers[0].Offset, 0, destructorOnly);
wrappedEntries, @class.Layout.VTablePointers[0].Offset, 0,
VTables.ItaniumOffsetToTopAndRTTI, destructorOnly);
}
private void AllocateNewVTableEntries(IList<VTableComponent> entries,
IList<VTableComponent> wrappedEntries, uint vptrOffset, int tableIndex, bool destructorOnly)
IList<VTableComponent> wrappedEntries, uint vptrOffset, int tableIndex,
int offsetRTTI, bool destructorOnly)
{
var pointerSize = Context.TargetInfo.PointerWidth / 8;
string suffix = (destructorOnly ? "_dtor" : string.Empty) +
(tableIndex == 0 ? string.Empty : tableIndex.ToString(CultureInfo.InvariantCulture));
int size = entries.Count;
uint pointerSize = Context.TargetInfo.PointerWidth / 8;
string vtptr = $"vtptr{suffix}";
// allocate memory for the new v-table
WriteLine($"var {vtptr} = Marshal.AllocHGlobal({size} * {pointerSize});");
// ensure it's automatically freed when no longer needed
WriteLine($"__handleManagedVTables.Add(new global::CppSharp.Runtime.SafeUnmanagedMemoryHandle({vtptr}, true));");
string vfptr = $"vfptr{suffix}";
// obtain a pointer in the table to the start of virtual functions
WriteLine($"var {vfptr} = {vtptr} + {offsetRTTI} * {pointerSize};");
var managedVTables = destructorOnly ? "__ManagedVTablesDtorOnly" : "__ManagedVTables";
WriteLine($"{managedVTables}[{tableIndex}] = {vfptr}.ToPointer();");
// fill the newly allocated v-table
for (var i = 0; i < entries.Count; i++)
{
var entry = entries[i];
var offset = pointerSize
* (i - (Context.ParserOptions.IsMicrosoftAbi ? 0 : VTables.ItaniumOffsetToTopAndRTTI));
var offset = pointerSize * (i - offsetRTTI);
var nativeVftableEntry = $@"*(void**) (new IntPtr(*(void**) {
Helpers.InstanceIdentifier}) + {vptrOffset} + {offset})";
string vfptr = $"vfptr{(destructorOnly ? "_dtor" : string.Empty)}{tableIndex}";
var managedVftableEntry = $"*(void**) ({vfptr} + {offset})";
if ((entry.Kind == VTableComponentKind.FunctionPointer ||
@ -1691,8 +1693,10 @@ namespace CppSharp.Generators.CSharp @@ -1691,8 +1693,10 @@ namespace CppSharp.Generators.CSharp
!entry.IsIgnored() &&
(!destructorOnly || entry.Method.IsDestructor ||
Context.Options.ExplicitlyPatchedVirtualFunctions.Contains(entry.Method.QualifiedOriginalName)))
// patch with pointers to managed code where needed
WriteLine("{0} = _Thunks[{1}];", managedVftableEntry, wrappedEntries.IndexOf(entry));
else
// and simply copy the rest
WriteLine("{0} = {1};", managedVftableEntry, nativeVftableEntry);
}
}
@ -2620,8 +2624,8 @@ namespace CppSharp.Generators.CSharp @@ -2620,8 +2624,8 @@ namespace CppSharp.Generators.CSharp
@class = (Class) method.OriginalFunction.Namespace;
if (Context.ParserOptions.IsMicrosoftAbi)
vtableIndex = @class.Layout.VFTables.IndexOf(@class.Layout.VFTables.Where(
v => v.Layout.Components.Any(c => c.Method == @virtual)).First());
vtableIndex = @class.Layout.VFTables.IndexOf(@class.Layout.VFTables.First(
v => v.Layout.Components.Any(c => c.Method == @virtual)));
WriteLine($@"var {Helpers.SlotIdentifier} = *(void**) ((IntPtr) {
(thisParam != null ? $"{thisParam.Name}."

4
tests/VTables/VTables.Tests.cs

@ -102,4 +102,8 @@ public class VTablesTests : GeneratorTestFixture @@ -102,4 +102,8 @@ public class VTablesTests : GeneratorTestFixture
Assert.That(foo.S, Is.EqualTo("test"));
}
}
[Test]
public void TestTypeNameRTTI() =>
Assert.That(BaseClassVirtual.TypeName, Does.EndWith(nameof(BaseClassVirtual)));
}

6
tests/VTables/VTables.cpp

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
#include "VTables.h"
#include <typeinfo>
Foo::Foo()
{
@ -66,6 +67,11 @@ BaseClassVirtual* BaseClassVirtual::getBasePtr() @@ -66,6 +67,11 @@ BaseClassVirtual* BaseClassVirtual::getBasePtr()
return new DerivedClassVirtual();
}
const char* BaseClassVirtual::getTypeName()
{
return typeid(BaseClassVirtual).name();
}
DerivedClassVirtual::DerivedClassVirtual()
{
}

7
tests/VTables/VTables.cs

@ -12,6 +12,13 @@ namespace CppSharp.Tests @@ -12,6 +12,13 @@ namespace CppSharp.Tests
{
}
public override void Setup(Driver driver)
{
base.Setup(driver);
driver.ParserOptions.EnableRTTI = true;
}
public override void SetupPasses(Driver driver)
{
driver.Context.TranslationUnitPasses.AddPass(new FunctionToInstanceMethodPass());

1
tests/VTables/VTables.h

@ -31,6 +31,7 @@ public: @@ -31,6 +31,7 @@ public:
virtual int retInt();
static BaseClassVirtual getBase();
static BaseClassVirtual* getBasePtr();
static const char* getTypeName();
};
class DLL_API DerivedClassVirtual : public BaseClassVirtual

5
tests/VTables/premake4.lua

@ -1,2 +1,5 @@ @@ -1,2 +1,5 @@
group "Tests/VTables"
SetupTestCSharp("VTables")
SetupTestCSharp("VTables")
project("VTables.Native")
rtti "On"
Loading…
Cancel
Save