D
Daniel Yelland
Hi,
I have developed a number of code libraries in Win32 DLLs and have written a
number of test suite executables that implicitly link to these libraries in
order to test them. In one of my test applications, which runs fine in Debug
mode, it is crashing in the destructor of a local object on the stack when
it is built in release mode.
An example of the C++ that causes the problem is as follows (apologies for
the contrived example): -
CTestSmartPointer : public CSmartPointer
{
public:
CTest() {;}
~CTest() {;}
void ReleaseObject() {throw _T("Released Object!");}
}
CTestSmartPointer kSmartPointer;
unsigned int uiIndex = 0;
for(uiIndex = 0; uiIndex < MAX_REFERENCES; uiIndex++)
{
kSmartPointer.IncrementReferenceCount();
}
try
{
for(uiIndex = 0; uiIndex < MAX_REFERENCES; uiIndex++)
{
kSmartPointer.DecrementReferenceCount();
}
}
catch(const TCHAR *)
{
bTestPassed = true;
}
CSmartPointer is a base class defined in one of the DLL libraries. The
exception is thrown as expected and the test is marked as passed. Once the
function exits, though the program crashes entering the destructor for the
CSmartPointer base class.
Now for the weirdness...
In attempting to track this problem down, I decided to pad the function with
_asm nop instructions to spot potential stack corruption. This fixed the
problem, but when I looked at the generated code I got a surprise...
Generated code with one trailing _asm nop at the end of the function: -
lea ecx,[kSmartPointer]
mov dword ptr [ebp-4],0FFFFFFFFh
mov dword ptr [kSmartPointer],offset
`CSmartPointerTestElement::RunSmartPointerTests'::`2'::CTest::`vftable'
(4032C0h)
call CSmartPointer::~CSmartPointer (402304h)
Contents of ECX = 0012FEC0
Address of object = 0012FEC0
Generated code with no _asm nop instructions: -
mov dword ptr [ebp-4],0FFFFFFFFh
mov dword ptr [kSmartPointer],offset
CSmartPointerTestElement::RunSmartPointerTests'::`2'::CTest::`vftable'
(4032C0h)
call CSmartPointer::~CSmartPointer (4022F4h)
Contents of ECX = 7C359270
Address of object = 0012FEC0
This problem does not occur when global optimisation are turned off for the
function (#pragma optimize("g" off)), so I assumed the compiler was making
assumptions about the contents of the ECX register not changing even in the
presence of exceptions (ECX is loaded with the address of the smart pointer
earlier in the file), however what I don't understand, is why adding an _asm
nop would cause the generation of another load instruction.
Is there anything I could be doing wrong in either my project settings or
C++/DLL declarations that could cause this behaviour?
If anyone has any information into the cause or resolution of this bug, it
would be greatly appreciated.
Regards,
Daniel Yelland
I have developed a number of code libraries in Win32 DLLs and have written a
number of test suite executables that implicitly link to these libraries in
order to test them. In one of my test applications, which runs fine in Debug
mode, it is crashing in the destructor of a local object on the stack when
it is built in release mode.
An example of the C++ that causes the problem is as follows (apologies for
the contrived example): -
CTestSmartPointer : public CSmartPointer
{
public:
CTest() {;}
~CTest() {;}
void ReleaseObject() {throw _T("Released Object!");}
}
CTestSmartPointer kSmartPointer;
unsigned int uiIndex = 0;
for(uiIndex = 0; uiIndex < MAX_REFERENCES; uiIndex++)
{
kSmartPointer.IncrementReferenceCount();
}
try
{
for(uiIndex = 0; uiIndex < MAX_REFERENCES; uiIndex++)
{
kSmartPointer.DecrementReferenceCount();
}
}
catch(const TCHAR *)
{
bTestPassed = true;
}
CSmartPointer is a base class defined in one of the DLL libraries. The
exception is thrown as expected and the test is marked as passed. Once the
function exits, though the program crashes entering the destructor for the
CSmartPointer base class.
Now for the weirdness...
In attempting to track this problem down, I decided to pad the function with
_asm nop instructions to spot potential stack corruption. This fixed the
problem, but when I looked at the generated code I got a surprise...
Generated code with one trailing _asm nop at the end of the function: -
lea ecx,[kSmartPointer]
mov dword ptr [ebp-4],0FFFFFFFFh
mov dword ptr [kSmartPointer],offset
`CSmartPointerTestElement::RunSmartPointerTests'::`2'::CTest::`vftable'
(4032C0h)
call CSmartPointer::~CSmartPointer (402304h)
Contents of ECX = 0012FEC0
Address of object = 0012FEC0
Generated code with no _asm nop instructions: -
mov dword ptr [ebp-4],0FFFFFFFFh
mov dword ptr [kSmartPointer],offset
CSmartPointerTestElement::RunSmartPointerTests'::`2'::CTest::`vftable'
(4032C0h)
call CSmartPointer::~CSmartPointer (4022F4h)
Contents of ECX = 7C359270
Address of object = 0012FEC0
This problem does not occur when global optimisation are turned off for the
function (#pragma optimize("g" off)), so I assumed the compiler was making
assumptions about the contents of the ECX register not changing even in the
presence of exceptions (ECX is loaded with the address of the smart pointer
earlier in the file), however what I don't understand, is why adding an _asm
nop would cause the generation of another load instruction.
Is there anything I could be doing wrong in either my project settings or
C++/DLL declarations that could cause this behaviour?
If anyone has any information into the cause or resolution of this bug, it
would be greatly appreciated.
Regards,
Daniel Yelland