• Advertisement
Sign in to follow this  

Overridden new/delete and classes in DLLs

This topic is 3968 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm working on adding Collada support to a project, using the COLLADA-DOM SDK. For various reasons we need to dynamically link the SDK rather than statically linking; this is, as I understand it, a relatively new feature in the SDK and not used very widely. For the most part everything works fine, except I've run into a problem with our custom overloaded global new/delete operators. The SDK exposes a class called DAE. Allocating an object with new DAE invokes our overridden global new. However, calling delete on that same pointer invokes the compiler-generated scalar delete in the DLL. Our custom new uses its own allocation strategy, so when the standard delete runs, it doesn't find the pointer to be valid, and crashes. Right now, I've solved this by manually invoking the destructor, and then directly freeing the memory with a call into the custom memory management library. This works fine - no leaks, the DLL's internal allocations are freed properly by the destructor, and everything is OK. However, because the SDK is "sealed code", I can't override delete for the class; this means that any client code that just deletes a DAE object will barf. Having to invoke a custom deleteDAE function is annoying. I don't really understand how the linkage rules are calling the correct overridden new but not the correct delete. In my Googling I came across a mention that __declspec(dllimport) and dllexport on the class's constructor/destructor should invoke some compiler/linker magic which prevents this issue; however, I've carefully checked that the __declspecs are present as expected, and yet the problem persists. Is there a way to force the correct delete (i.e. overridden version in the EXE) to be called? If not, can anyone at least explain how this weird situation is possible?

Share this post


Link to post
Share on other sites
Advertisement
One approach would be to hook the iat in the dll so that it's routines called your custom routines. Google on "iat hooking" for references to that topic.

Share this post


Link to post
Share on other sites
Hooking the SDK's allocations is massive overkill. I don't care what the DLL does with its memory internally; it's also not worth the time and infrastructure to muck around that deeply when the current solution is fine. (For clarification, the DAE object is the only thing from the DLL that I need to manually allocate; for everything else it's fine for the SDK to do its own thing internally, which means the typical DLL/EXE heap boundary issues with allocations are not applicable.)

I literally have a two-line function that allocates and immediately deletes an object; tracing through the code steps into our custom operator ::new, and then invokes the standard operator ::delete in the DLL's code. I have no idea how this is possible.

What I'm primarily interested in is understanding how the linkage semantics allow this to happen. Based on my current understanding, I should be seeing both new and delete invoked in the same codespace, whether that's the EXE or the DLL (and, frankly, I don't care who actually allocates the DAE objects; as long as the same thing frees them I'm happy).

Like I said, I have a working solution - if I have to call a special function to free the object, it's not that big of a deal. I'd just like to know what's going on.



Thanks for the suggestion, though [smile]

Share this post


Link to post
Share on other sites
Yes, I'd like to know what's going on, too.


I have a similiar problem in another context. Not game developing, not Collada.

I write a dll A and link it to another dll B.
If I mark the class exports in the header file with __declspec(dllexport)/__declspec(dllimport) everything works fine. If I export the functions explicitely with a .def file, I get the same behaviour as ApochPiQ.

In the calling dll B, new is called from mfc80u.dll.
But delete calls the "deleting destructor()" from exporting dll A.

Call stack for new with __declspec(dllexport)/__declspec(dllimport) AND with .def-File:

* B.dll!CallingFunction?::defaultConstructor() (for new CalledClass?())
* mfc80u.dll!operator new(unsigned int nSize=24)


Call stack for delete with __declspec(dllexport)/__declspec(dllimport)
* B.dll!CalledClass::'scalar deleting destructor'()
* mfc80u.dll!operator delete[](void *p=0xADDRESS)

Call stack for delete with .def-File
* A.dll!CalledClass::'scalar deleting destructor'()
* LinkedMemoryManagement.dll!operator delete(void *p=0xADDRESS)

Perhabs ApochPiQ problem results from the same issue. I would really be interested if someone has an explanation.

Share this post


Link to post
Share on other sites
ApochPiQ, I ran into something similar with mfc using VS 2005. Seems if I did a new on any object based on CObject, my own global operator new was called, but my own global operator delete was not.

After creating a testbed MFC app to examine this behavior, I was pleasantly suprised to discover if I derive a new class of my own from a CObject-based class, that both my own global operator new and my own global delete operator were now called.

So, you could try creating your own object based on DEA by deriving from it, then see if this does not magically use your global operator new and global operator delete in the correct fashion.

Why does this happen at all? Well, that's certainly a good question.

Some extra details for others who encounter this behavior with MFC, maybe I can help you out. When I looked at the source for the debug version of the CObject new and delete handling, I discovered much to my horror that in a certain case, the code is simply written wrong. You see, Microsoft uses a class version of operator new and operator delete to override any custom global operator new and delete for debugging purposes. In a debug build, this works fine because they turn around and call a special overloaded new that takes 4 parameters. As long as you do not have one of these defined (and I sure didn't) the class uses this special operator new. Unfortunately, the class operator delete just calls free(). That's right, you heard me, the class overloaded operator new ends up calling an overloaded global new operator, but the class operator delete just calls free. Why not call the global operator delete? Well, it gets worse, because when the debug code is not active, the class operator new just calls the global operator new with a single size parameter (because there is no debug memory tracking and so no extra parameters) but of course the class operator delete once again just calls free. Now, I do not have source to the release build of afxmem.cpp so I don't really know how it is implemented in the release version of the .dll, but it behaves very, very much like this disabled debug case which causes me to suspect it has the same bad behavior.

Why does deriving a class fix this? Wish I knew for sure. That would mean I fully understood this. Perhaps by deriving a new class the CObject class' operator new and operator delete are lost so the global operator new and delete are called? Sorry to say I just don't know.

If deriving your own object from the "bad" object still does not work you could also try creating your own class operator new and delete (fight fire with fire) then redirect to the global operator new and delete from each.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement