Question

I'm trying to start a tutorial about libclang library, but I get an access violation when calling the function clang_getSpellingLocation(). Other information about the error is correctly reported with error count, line and column.

My environment: C++Builder XE pro, Windows 7 32bit, LLVM 3.4, libCLang.lib converted using coff2omf, libCLang.dll.

I tested the same code on visual C++ 2010 and it works correctly.

Please could anyone you help me about this issue?

My simple code

//---------------------------------------------------------------------------
void __fastcall TForm8::Button1Click(TObject *Sender)
{

    unsigned line, column;
    CXIndex index = clang_createIndex(0, 0);

    const  char  * args []  =  {
    "-I/usr/include" ,
    "-I."
    };

    int  numArgs  =  sizeof ( args )  /  sizeof ( * args );

  CXTranslationUnit tu = clang_parseTranslationUnit(index, "G:\\projects\\LibCLang  \\File2.cpp", args, numArgs, NULL, 0, CXTranslationUnit_None);

  unsigned  diagnosticCount  =  clang_getNumDiagnostics ( tu );

  for ( unsigned  i  =  0 ;  i  <  diagnosticCount ;  i++ )
  {
      CXDiagnostic  diagnostic   =  clang_getDiagnostic ( tu ,  i );
      CXSourceLocation location = clang_getDiagnosticLocation(diagnostic);
      clang_getSpellingLocation(location, NULL, &line, &column, NULL);
      CXString text = clang_getDiagnosticSpelling(diagnostic);
      UnicodeString s = clang_getCString(text);
  }
}
Was it helpful?

Solution

Are you sure the AV is on clang_getSpellingLocation() and not clang_getDiagnosticSpelling()?

When using the __cdecl calling convention, compilers from different vendors are allowed to do whatever they want when it comes to returning a structure by value when the size of the structure is <= 8 bytes. In the case of clang_getDiagnosticSpelling(), CXString is 8 bytes in a 32bit environment. Some compilers, like Visual C++, return the contents of an 8-byte structure directly in the EAX:EDX CPU registers, whereas other compilers, like C++Builder, use a hidden output parameter that passes a reference to a temp structure.

clang_getDiagnosticSpelling() (and other similar functions) return a CXString using EAX:EDX, but C++Builder uses a hidden parameter instead. There is a simple workaround for that. C++Builder expects a __cdecl function to return an __int64 using EAX:EDX, and since CXString and __int64 are the same size in 32bit, you can do the following:

typedef __int64 __cdecl (*clang_getDiagnosticSpellingHack)(CXDiagnostic);

//CXString text = clang_getDiagnosticSpelling(diagnostic);
clang_getDiagnosticSpellingHack func = reinterpret_cast<clang_getDiagnosticSpellingHack>(&clang_getDiagnosticSpelling);
__int64 tmp = func(diagnostic);
CXString text = reinterpret_cast<CXString&>(tmp);

Alternatively, you can utilize C++Builder's _EAX and _EDX intrinsics:

typedef void __cdecl (*clang_getDiagnosticSpellingHack)(CXDiagnostic);

//CXString text = clang_getDiagnosticSpelling(diagnostic);
clang_getDiagnosticSpellingHack func = reinterpret_cast<clang_getDiagnosticSpellingHack>(&clang_getDiagnosticSpelling);
CXString text;
func(diagnostic);
text.ptr_data = (void*) _EAX;
text.int_data = _EDX;

As for clang_getSpellingLocation(), I would not expect there to be such a mismatch in conventions since it has no return value, unless clang_getSpellingLocation() were compiled to accept its CXSourceLocation parameter differently than how C++Builder is passing it. C++Builder pushes all three data values from CXSourceLocation directly on the call stack. You would have to look at the disassembly of clang_getSpellingLocation() to see how it is actually accessing the values of its CXSourceLocation parameter, and then adjust your C++Builder code accordingly if needed.

You should also double-check how clang_getDiagnosticLocation() is returning its CXSourceLocation to make sure that it matches how C++Builder expects it to be returned (via a hidden output parameter by reference) to ensure that memory is not being corrupted before clang_getSpellingLocation() is being called. So it is possible that clang_getSpellingLocation() itself might not be the culprit. Hard to say without seeing the disassembly for both clang_getDiagnosticLocation() and clang_getSpellingLocation() (I have seen the disassembly for functions that return CXString, that is how I know for sure that clang uses EAX:EDX for that).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top