0

I'm trying to compile C++ code as managed dll. I was able to compile it using this compiler and linker options:

Compiler:

/TP /analyze- /W3 /Zc:wchar_t /Zi /Od /sdl- /Fd"Debug\vc140.pdb" /Zc:inline /fp:precise /D "_SCL_SECURE_NO_WARNINGS" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_WINDLL" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /Oy- /clr:pure /FU"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\mscorlib.dll" /MDd /Fa"Debug\" /nologo /Zl /Fo"Debug\" /Fp"Debug\aaa.pch"

Linker:

/OUT:"C:\Out\x86\Debug\aaa.dll" /MANIFEST /NXCOMPAT /PDB:"C:\aaa.pdb" /DYNAMICBASE "ucrtd.lib" "vcruntimed.lib" "libvcruntimed.lib" "msvcurtd.lib" "msvcrtd.lib" "libcmtd.lib" "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" /FIXED:NO /DEBUG /DLL /CLRIMAGETYPE:PURE /INCREMENTAL:NO /PGD:"C:\Out\x86\Debug\aaa.pgd" /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /ManifestFile:"Debug\aaa.dll.intermediate.manifest" /ERRORREPORT:PROMPT /NOLOGO /ASSEMBLYDEBUG /TLBID:1

The problem I have is really strange. I have a pure abstract base class and a derived class in that dll. When I create an instance of that derived class with new, I can see that it's vtable is filled with zeros.

So I can't call any virtual function.

I couldn't recreate this issue with dummy console application, so I have no idea, what can be the reason of this. As far as I see from dummy application, there is no calls to crt or some obscure library to fill vtable with correct addresses, there is just a couple of assembly lines.

UPDATE:

The problem in the code looks like this:

class Connector : public IConnector
{
   public:
       Connector() : m_a(0), m_b(0)
       {}
   <...>
};

<...>

Connector * pc = new Connector();
Connector c;

IConnector has default ctor and no fields, so it does nothing.

In the watch window I can see that vtable pointers in pc and c are set to something nonzero and they are the same. But the place they point to is filled with zeros.

IMHO it doesn't look like vtable pointer is broken, it looks like vtable itself is not initialized.

Amomum
  • 6,217
  • 8
  • 34
  • 62
  • There is no law that says that a C++ bug that corrupts an internal structure must involve "calls to crt or some obscure library". You have a bug somewhere in your code. There's no other answer to your question except "find your bug, and fix it". – Sam Varshavchik Aug 23 '17 at 11:08
  • @SamVarshavchik Well, great, but this code worked when it was compiled to unmanaged dll. Can you please tell me _how_ could I fill all vtable with zeros? I have no idea where to look. – Amomum Aug 23 '17 at 11:10
  • Well, neither is there a law that says there's only one possible way to corrupt memory. Anywhere an uninitialized or a dangling pointer is written to, anywhere there's "undefined behavior" of any kind. That's what "undefined behavior" means. Your bug can be anywhere in your code, there's no law that requires a particular bug to always happen in some specific way. – Sam Varshavchik Aug 23 '17 at 11:49
  • @SamVarshavchik i'm looking in the disassembly and I'm not seeing vtable initialization. So memory is correct and then corrupted, it's just zeros from the very beginning of the ctor call. And it happens to all of the classes that has virtual functions. That doesn't look like _that_ kind of UB to me. – Amomum Aug 23 '17 at 11:58
  • Then, whatever is supposed to initialize the vtable does not occur due to some undefined behavior, that's all. Or the actual Vtable is somewhere else and you're looking at the wrong place. Or the class instance's vtable pointer itself is corrupted. The possibilities are endless. – Sam Varshavchik Aug 23 '17 at 12:05
  • 1
    @SamVarshavchik: I'm not agreeing with your hunch. Isn't a vtable (the table itself) read-only? The vtable _pointer_ of an object can change (normal during construction) but vtables belong to classes, not objects. They'd be read-only, like code, and as a result the write bit on the page shouldn't be set. You should get an AV if you tried to zero it. [Pedant note: this applies to MSVC, other C++ implementations may vary]. My hunch: real vtable is OK, vtable pointer isn't – MSalters Aug 23 '17 at 12:14
  • @MSalters I added clarification to the question. I don't think that vtable pointer is getting broken. – Amomum Aug 23 '17 at 13:15
  • @Amomum: Almost there, I'd say. Don't post code that "looks like" the problem. Reduce your problem to the absolute minimum to reproduce it, then post that. Even for large code bases, this can be done effectively by bisecting (remove 50% of the code, check if the problem is still there. You can remove 99.9% of the code in 10 steps that way) – MSalters Aug 23 '17 at 13:19
  • @MSalters I think I have found the problem. I answered myself. – Amomum Aug 23 '17 at 15:05

1 Answers1

0

Okay, I found the problem. To create purely managed dll I used this answer Creating a pure MSIL assembly from a C++/CLI project?

Step 6 suggested adding this snippet:

#pragma warning(disable:4483)
void __clrcall __identifier(".cctor")() { }

I'm not very good with C# so I just copied it without any understanding. That got rid of some errors at this point so when everything built I just left it there and ignored warnings like this (since I couldn't google anything about them):

Warning LNK4210 .CRTMA section exists; there may be unhandled static initializers or terminators 

(I know that I should have mentioned it in the question but it's too easy to forget about ignored warning. Ignoring warnings is bad, m'kay?)

Apparanetly this something is called 'module contstructor' and it does many important things like initializing global and static variables and, for that particular matter, vtables.

Looks like the author of that answer didn't use any of that so empty ctor worked for him.

I just removed this empty module ctor and vtables are not empty now.

Amomum
  • 6,217
  • 8
  • 34
  • 62