Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: C0000005 with a class derived from System::Exception with clang compilers



Permlink Replies: 10 - Last Post: Feb 3, 2017 3:40 AM Last Post By: Andy Walker
Arkady Semylio

Posts: 87
Registered: 9/18/15
C0000005 with a class derived from System::Exception with clang compilers
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 2, 2017 6:06 AM
Hi all,

I'm doing some code testing for future porting of old codebases on new compilers.
Along the road, I'm faced with an annoying problem related to a very simple piece
of code involving exception handling.

It'ss a very simple VCL project whose purpose is to test the selective catch of some
user defined exception classes. So, I've reduced the testing code to a minimal size.

//---------------------------------------------------------------------------
 
#include <vcl.h>
#pragma hdrstop
 
#include "Unit1.h"
 
//---------------------------------------------------------------------------
 
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
 
//---------------------------------------------------------------------------
 
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------
 
class ETest : public Exception {
public:
    ETest() : Exception( _T( "Ouch!" ) ) {}
};
 
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    try {
        //throw Exception( _T( "Bingo!" ) );
        throw ETest();
    }
    catch ( Exception const & E ) {
        ShowMessage( E.Message ) ;
    }
}
//---------------------------------------------------------------------------


With the above code I've got a C0000005 throwing an object (a class derived
from System::Exception) using clang compilers for Windows.

First chance exception at $00656FCE. Exception class $C0000005 with message 'access violation at 0x00656fce: read of address 0xffffffb8'. Process Project1.exe (5084)

The old BCC32 handles the flow correctly (but, alas, the old compiler suffer from
leaks while throwing exceptions; see https://goo.gl/xBCvEz).

Also BCC64 seems broken. Any build configuration causes the problem.

Any clue?

Bye bye
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: C0000005 with a class derived from System::Exception with clangcompilers
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 2, 2017 10:55 AM   in response to: Arkady Semylio in response to: Arkady Semylio
Arkady wrote:

class ETest : public Exception {
public:
ETest() : Exception( _T( "Ouch!" ) ) {}
};

Use the '__fastcall' calling convention (and consider also using 'inline'),
and use _D() instead of _T():

class ETest : public Sysutils::Exception {
public:
    inline __fastcall ETest() : Exception( _D( "Ouch!" ) ) {}
};


Look in System.SysUtils.hpp for examples of how native VCL Exceptions are
delcared in C++.

With the above code I've got a C0000005 throwing an object (a class
derived from System::Exception) using clang compilers for Windows.

First chance exception at $00656FCE. Exception class $C0000005 with
message 'access violation at 0x00656fce: read of address 0xffffffb8'.
Process Project1.exe (5084)

0xffffffb8 is -72, which likely means some piece of code is trying to access
data at offset -72 bytes from a NULL pointer. In RTL objects that are derived
from TObject (like Sysutils::Exception), -72 happens to be the byte offset
of an object's VMT entry for the virtual TObject::ToString() method. So,
this makes me wonder if something in BCC64's exception handling mechanism
is trying to call ToString() on an Exception object but has a NULL pointer
to the object, or at least to its VMT.

What code exists at address 0x00656fce? Use the debugger to jump to that
address and look at the code there, that will tell you narrow down what is
actually being accessed and when. Try enabling Debug DCUs in yor project
options so you can step through the RTL's source code.

--
Remy Lebeau (TeamB)
Arkady Semylio

Posts: 87
Registered: 9/18/15
Re: C0000005 with a class derived from System::Exception with clangcompilers
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 2, 2017 11:19 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
Arkady wrote:

Use the '__fastcall' calling convention
Why? I'm calling the ctor from C++... __fastcall isn't usefut at all.

But alas, also your advice for _D() doesn't help.

inline __fastcall ETest() : Exception( _D( "Ouch!" ) ) {}
Inline, __fastcalll etc etc etc doesn't help

Look in System.SysUtils.hpp for examples of how native VCL Exceptions are
delcared in C++.
Neither redeclaring all ctor of the base Exception or wirting the custom
exception in directly in delphi doesn't help.

What code exists at address 0x00656fce? Use the debugger to jump to that
...
options so you can step through the RTL's source code.

get_delphi_type_from_object:
00657430 8B442404         mov eax,[esp+$04]
00657434 8B00             mov eax,[eax]
00657436 8B40B8           mov eax,[eax-$48]   <--- AV HERE
00657439 83C0FC           add eax,-$04
0065743C C3               ret 


It's clearly a BUG in the libs or compiler. If the exception is derived
from another base or has not base at all the problem does't arise.

The OLD compiler works well... (but, alas, create leaks)

Now I'll use a my own hierarchy for my exceptions, but THIS IS A BUG, period!

Thaks for your efforts.

Bye bye
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: C0000005 with a class derived from System::Exception withclangcompilers
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 2, 2017 1:07 PM   in response to: Arkady Semylio in response to: Arkady Semylio
Arkady wrote:

Use the '__fastcall' calling convention
Why?

Because that is what the inherited constructor uses.

But alas, also your advice for _D() doesn't help.

Using _T() is wrong because _T() is based on TCHAR, which the RTL does not
use at all. _T() maps the input literal to either 'char' or 'wchar_t' depending
on what TCHAR maps to, which can be set in the project options. You can
pass both char* and wchar_t* strings to System::String, but the first will
incur a runtime conversion whereas the second will not.

_D() is similar to _T(), but is instead based on the encoding of System::Char
and System::String, not TCHAR, so _D() will map the input literal to the
RTL's native encoding, whatever it happpens to be.

get_delphi_type_from_object:
00657430 8B442404 mov eax,[esp+$04]
00657434 8B00 mov eax,[eax]
00657436 8B40B8 mov eax,[eax-$48] <--- AV HERE

Hex $48 is decimal 72. This code is expecting ESP+4 to hold an object pointer,
which gets MOV'ed into EAX, and then dereferenced to access the object's
VMT pointer and MOV that back into EAX. At this point, EAX is 0, meaning
the VMT pointer is NULL. Then the memory address in EAX is subtracted by
72 bytes (which is the wrong size to subtract!) and dereferenced to retreive
a specific function pointer from the vtable. But since EAX is 0, and -72
is not a valid memory address, an AV occurs.

Here is the source code for get_delphi_type_from_object() (see $(BCB)\source\cpprtl\Source\libcxxabi\win64\seh_unwind\src\delphi_support.cpp):

#define POINTER_ADD(P,I) (((char *)(P)) + (I))
 
static delphi_vmt_t const *get_vmt(void const *o) {
//    return (delphi_vmt_t const *)((char *)(*(char **)o) - sizeof(delphi_vmt_t));
    return (delphi_vmt_t const *)POINTER_ADD(*(char **)o, -sizeof(delphi_vmt_t));
}
 
void const*get_delphi_type_from_object(void const *o) {
    delphi_vmt_t const *vmt = get_vmt(o);
    return (void const *)POINTER_ADD(vmt->vmtTypeInfo, -sizeof(void *));
}


And here is how get_delphi_type_from_object() is called (in exception_handler::handle_action_value(),
see $(BCB)\source\cpprtl\Source\libcxxabi\win64\seh_unwind\src\personality.cpp):

__cxa_exception* exception =
  __to_cpp_exception(unwind_exception);
uintptr_t type = (uintptr_t)exception->exceptionType;
 
DEBUG_PRINT("cpprtl.unwind.search", ("exception type = %p\n", type));
 
if (type == 0) {
	  ms_api::exception_record &x_rec =
	      *(ms_api::exception_record *)__cxa_get_exception_ptr(unwind_exception);
	  if (ms_api::is_delphi_exception(x_rec)) {
	  /*
	    It was a Delphi exception, received directly from the Delphi RTL.  We 
have to
	    delve into the delphi exception object itself to get the type info.
	   */
	      type = (uintptr_t)get_delphi_type_from_object(ms_api::get_delphi_exception(x_rec));
	  }
//		  ms_api::get_delphi_exception(*(ms_api::exception_record *)__cxa_get_exception_ptr(unwind_exception)));
}


Since ms_api::is_delphi_exception() is returning true (it is just returning
whether the exception record's ExceptionCode field is 0xEEDFADE or not),
this means:

1. ms_api::get_delphi_exception() is returning an invalid object pointer.
Which is unlikely, since the original Exception object pointer is stored
directly in the exception record that gets thrown. Unless the 'throw' statement
is not populating the exception record correctly to begin with, which is
doubtful. On the other hand, Delphi objects never have a null VMT pointer,
either.

2. the delphi_vmt_t struct (see $(BCB)\source\cpprtl\Source\libcxxabi\win64\seh_unwind\src\delphi_support.h)
is not be being handled correctly, causing offsets to be subtracted incorrectly.
get_delphi_type_from_object() code is trying to access the VMT's entry
for the object's TypeInfo() function, but that is not located at byte offset
-72, in 32bit or 64bit, so I have no idea why the compiled code is subtracting
72 from the VMT's base address.

It's clearly a BUG in the libs or compiler. If the exception is
derived from another base or has not base at all the problem does't
arise.

Now I'll use a my own hierarchy for my exceptions, but *THIS IS A
BUG*, period!

Feel free to file a bug report in QualityPortal.

--
Remy Lebeau (TeamB)
Arkady Semylio

Posts: 87
Registered: 9/18/15
Re: C0000005 with a class derived from System::Exception withclangcompilers
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 2, 2017 2:58 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
Use the '__fastcall' calling convention
Why?

Because that is what the inherited constructor uses.

Is not important. The C++ code call a C++ ctor which, in turn,
call the inherited ctor with the right convention call.

Moreover, using __fastcall in C++ result in a slightly slower code:

Quote from the link below:

Conclusion

"To repeat what I have already said, I advise that you never use __fastcall unless it is specifically
required by the VCL. Using __fastcall isn’t necessary, it clutters up your code, and it appears
to execute a bit slower than the default calling convention."

See: https://goo.gl/8XLVQA


But alas, also your advice for _D() doesn't help.

Using _T() is wrong because _T() is based on TCHAR, which the RTL does not
[snip]
RTL's native encoding, whatever it happpens to be.

It's irrelevant to the problem regarding this thread. I can't change the entire codebase
already written 'cause there's no time resources for that work. Moreover the code is
used to verify a bug in the compiler. _T() or _D() doesn't chage the stare of affairs,
while the presence of this bug is a critical fact.


get_delphi_type_from_object:
00657430 8B442404 mov eax,[esp+$04]
00657434 8B00 mov eax,[eax]
00657436 8B40B8 mov eax,[eax-$48] <--- AV HERE

Hex $48 is decimal 72. This code is expecting ESP+4 to hold an object pointer,
[snip] [snip] [snip]
72 from the VMT's base address.

Ok, I obviously appreciate the use of your time for this explanation... but... as far as I can see
this is useful only to confirm that the compiler produces wrong code or the libraries are broken.
But I can't do the work either of library team or compiler team of EMB, analyzing what is broken
in a complex tool as RAD Studio. I'm evaluating a Trial Version in order to suggest if it's worth
to upgrade or not.

Feel free to file a bug report in QualityPortal.

Done several hours ago: https://quality.embarcadero.com/browse/RSP-16811

Bye bye
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: C0000005 with a class derived from System::Exceptionwithclangcompilers
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 2, 2017 5:12 PM   in response to: Arkady Semylio in response to: Arkady Semylio
Arkady wrote:

[Using _T() is] irrelevant to the problem regarding this thread. I can't
change the entire codebase already written 'cause there's no time
resources for that work.

A simple find and replace will usually do fine.

Moreover the code is used to verify a bug in the compiler. _T() or _D()
doesn't chage the stare of affairs, while the presence of this bug is a
critical fact.

Perhaps so, but leaving _T() in place does leave another silent bug in your
code, which might come back to bite you later on. Just saying.

--
Remy Lebeau (TeamB)
Arkady Semylio

Posts: 87
Registered: 9/18/15
Re: C0000005 with a class derived from System::Exception withclangcompilers
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 2, 2017 4:44 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:

Feel free to file a bug report in QualityPortal.

For the record, Andy's suggestion produced such workaround:

class ETest : public Exception {
public:
    //ETest() : Exception( _T( "Bad!" ) ) {}
    ETest( String Dummy = String() ) : Exception( _T( "Nice!" ) ) {}
};


Passing a dummy (empty by default) String parameter seems to cancel the AV.

Bye bye
Andy Walker

Posts: 72
Registered: 1/20/01
Re: C0000005 with a class derived from System::Exception with clang compilers
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 2, 2017 3:44 PM   in response to: Arkady Semylio in response to: Arkady Semylio
I'm not sure whether you are interested in a workaround or not but the following seems to work;

 //---------------------------------------------------------------------------
 
 #include <vcl.h>
 #pragma hdrstop
 
 #include "Unit1.h"
 
 //---------------------------------------------------------------------------
 
 #pragma package(smart_init)
 #pragma resource "*.dfm"
 TForm1 *Form1;
 
 //---------------------------------------------------------------------------
 
 __fastcall TForm1::TForm1(TComponent* Owner)
     : TForm(Owner)
 {
 }
 //---------------------------------------------------------------------------
 
 class ETest : public Exception {
 public:
     ETest(const System::UnicodeString Msg="Ouch!") : Exception( _T( Msg) ) {}
 };
 
 void __fastcall TForm1::Button1Click(TObject *Sender)
 {
     try {
         //throw Exception( _T( "Bingo!" ) );
         throw ETest();
     }
     catch ( Exception const & E ) {
         ShowMessage( E.Message ) ;
     }
 }
 //---------------------------------------------------------------------------
 


I know this doesn't excuse the bug but it might get you out of a hole.

Andy
Arkady Semylio

Posts: 87
Registered: 9/18/15
Re: C0000005 with a class derived from System::Exception with clang compilers
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 2, 2017 3:56 PM   in response to: Andy Walker in response to: Andy Walker
Andy Walker wrote:
I'm not sure whether you are interested in a workaround or not but the following seems to work;

Hi Andy,

very interesting... thank you very much!

:-)

Bye bye
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: C0000005 with a class derived from System::Exception with clangcompilers [Edit]
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 2, 2017 5:02 PM   in response to: Andy Walker in response to: Andy Walker
Andy wrote:

ETest(const System::UnicodeString Msg="Ouch!") : Exception( _T(Msg) ) {}

You can't use the _T() macro (or the _D() macro) with variables, only with
literals. The only way the compiler would not be generating an error on
this code is if you have TCHAR mapped to 'char' instead of 'wchar_t' in the
project options. Switch the mapping to 'wchar_t' and the code will fail
to compile with an "unknown identifier" error, because it would then be trying
to pass an unknown 'LMsg' variable to the base Exception constructor. When
TCHAR is mapped to 'wchar_t', _T() (and TEXT()) prepends an 'L' to whatever
you pass in. This is intended to turn character/string literals into Unicode.
It is not intended for variable names.

You are using the macro in the wrong place, it should be like this instead:

ETest(const System::UnicodeString Msg = _T("Ouch!")) : Exception( Msg ) {}


But, like I said earlier, you really should be using _D() instead of _T()
for any literals that are used with Delphi-based native strings, eg:

ETest(const System::UnicodeString Msg = _D("Ouch!")) : Exception( Msg ) {}


_T() and _D() are similar, but they are NOT the same thing.

--
Remy Lebeau (TeamB)
Andy Walker

Posts: 72
Registered: 1/20/01
Re: C0000005 with a class derived from System::Exception with clangcompilers [Edit]
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 3, 2017 3:40 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Apologies, that was actually a typo as I re-keyed the code rather than copy and pasted it!

The workaround I tested was actually;

ETest(const System::UnicodeString Msg = "Ouch!") : Exception( Msg ) {}


Agree that your suggestion is the proper way to do it though!

Andy
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02