Community
cancel
Showing results for 
Search instead for 
Did you mean: 
dan5
Beginner
129 Views

Access Violation copying a static instance in a dll

Hello,

So I've been looking to upgrade our product to use the latest version of the Intel Compiler (Composer 2011 update7) on Windows using Visual Studio 2010, and have hit a rather horrible compiler bug.

I've managed to recreate the problem in a simple test case which I've attached, but here's the basic problem:

We have a program that loads a shared DLL containing a class definition TestClass, which contains 16 floats within it as well as a constant static instance:


class TestClass
{

TestClass() {}
TestClass(float a, float b, float c, float d,
float e, float f, float g, float h,
float i, float j, float k, float l,
float m, float n, float o, float p) {

a00 = a; a01 = b; a02 = c; a03 = d;
a10 = e; a11 = f; a12 = g; a13 = h;
a20 = i; a21 = j; a22 = k; a23 = l;
a30 = m; a31 = n; a32 = o; a33 = p;
}

static const TestClass _staticInstance;

void initialise()
{
//memcpy(this, &_staticInstance, sizeof(TestClass));
//Using memcpy fixes the problem, as well as using a static scoped to this function, or not having the function inline
*this = _staticInstance;
}

float
a00, a10, a20, a30,
a01, a11, a21, a31,
a02, a12, a22, a32,
a03, a13, a23, a33;
};

This DLL then loads another DLL, which creates an instance of this class followed by calling the TestClass::initialise
() function.

void TestFunction()
{
TestClass t;
memset(&t, 0, sizeof(t));

printf("TestClass a00 value %f", t.a00);
t.initialise();
printf("TestClass a00 value %f", t.a00);
}

The problem is that this causes an access violation exception. Looking at the disassembly, it seems to be doing something a little funky...

TestFunction();
000007FEFC481007 mov eax,40h
000007FEFC48100C lea rsi,[7FEFC480000h] - What is this?
000007FEFC481013 mov qword ptr [rsp+rax+18h],rdx
000007FEFC481018 mov qword ptr [rsp+rax+10h],rdx
000007FEFC48101D mov qword ptr [rsp+rax+8],rdx
000007FEFC481022 mov qword ptr [rsp+rax],rdx
000007FEFC481026 sub rax,20h
000007FEFC48102A jne PluginEntryPoint+13h (7FEFC481013h)
000007FEFC48102C cvtss2sd xmm1,dword ptr [rsp+20h]
000007FEFC481032 movd rdx,xmm1
000007FEFC481037 lea rcx,[string "TestClass a00 value %f" (7FEFC48211Ch)]
000007FEFC48103E call qword ptr [__imp_printf (7FEFC4820C0h)]
000007FEFC481044 mov r11d,40h
000007FEFC48104A mov rax,qword ptr [__imp_TestClass::_staticInstance (7FEFC482000h)] - loads the address into register
000007FEFC481051 lea r9,[rsi+rax] - Why are we adding anything to the staticInstance pointer?!
000007FEFBB91055 mov rdx,qword ptr [r9+r11-8] <<================== Crashes here
000007FEFBB9105A mov rcx,qword ptr [r9+r11-10h]
...

For some reason, the intel compiler is loading the absolute address of TestClass::_staticInstance into register rax, but then adding some value stored in register rsi to this address, before attempting to use this new address to do a load of the first float into memory. As the value in rsi is a large value, this then causes an access violation exception.

Compiling the exact same TestFunction in the Loading DLL doesn't cause the crash, this also fixes the issue:
  • turning off optimizations using a pragma
  • changing the function to use memcpy
  • having the static defined in the function
  • changing the function to not be inlined
  • changing the number of floats within the class
  • using the Visual Studio compiler
Any help much appreciated

Thanks

Tom Ward

Nuke Software Engineer, The Foundry
The Foundry, 1 Wardour Street, London W1D 6PA, UK
Tel: +44 (0)20 7434 0449 - Fax: +44 (0)20 7434 1550 - Web : www.thefoundry.co.uk

The Foundry Visionmongers Ltd - Registered in England and Wales No: 4642027

0 Kudos
6 Replies
SergeyKostrov
Valued Contributor II
129 Views

Note: I was unable to extract your Test-Case from the RAR-archive.

Hi Tom,

I tested your Test-Case without DLLs and it worked. Here is my Test-Case with small modifications:

class TestClass
{
public:
TestClass()
{
a00 = 00.0f; a01 = 01.0f; a02 = 02.0f; a03 = 03.0f;
a10 = 10.0f; a11 = 11.0f; a12 = 12.0f; a13 = 13.0f;
a20 = 20.0f; a21 = 21.0f; a22 = 22.0f; a23 = 23.0f;
a30 = 30.0f; a31 = 31.0f; a32 = 32.0f; a33 = 33.0f;
};

TestClass(
float a, float b, float c, float d,
float e, float f, float g, float h,
float i, float j, float k, float l,
float m, float n, float o, float p )
{
a00 = a; a01 = b; a02 = c; a03 = d;
a10 = e; a11 = f; a12 = g; a13 = h;
a20 = i; a21 = j; a22 = k; a23 = l;
a30 = m; a31 = n; a32 = o; a33 = p;
};

static TestClass _staticInstance; // Version 1
// static TestClass *_staticInstance; // Version 2

void initialise()
{
// memcpy( this, &_staticInstance, sizeof(TestClass) );
*this = _staticInstance; // Version 1
// _staticInstance = this; //Version 2
};

float a00, a10, a20, a30,
a01, a11, a21, a31,
a02, a12, a22, a32,
a03, a13, a23, a33;
};

TestClass TestClass::_staticInstance; // Version 1
//TestClass *TestClass::_staticInstance = NULL; // Version 2

void TestFunction();

void TestFunction()
{
TestClass t;

// memset( &t, 0, sizeof( t ) );

printf( "TestClass a33 value %f\n", t.a33 );
t.initialise();
printf( "TestClass a33 value %f\n", t.a33 );
}

Outputs:

Version 1:
TestClass a33 value 33.000000
TestClass a33 value 33.000000

Version 2:
TestClass a33 value 33.000000
TestClass a33 value 33.000000

It is not clear what is going on and I would try to call the 't.initialise()' with all internals commented.

Also, I didn't see an initialization for the static member '_staticInstance'of the class in your Test-Case( posted on the web ), something like:

...
TestClass TestClass::_staticInstance;
...

I wonder how you've managed to compile the Test-Case?

Best regards,
Sergey

dan5
Beginner
129 Views

Hi Sergey,

I could also only reproduce when this class was defined in a shared dll and called from another dll, which is why you presumably couldn't reproduce (the rar file attached doesn't include any binaries, so you'll have to compile a release version from the included Visual Studio Solution. I've not tested the equivlent on any other platforms)

As contained in the attached rar file, the static instance is defined in the TestCase.cpp as such:

TestClass.cpp
//=============================

#include "TestClass.h"

D_IMPORTEXPORT_API const TestClass TestClass::_staticInstance(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);

//=============================

The changes you made in your post doesn't produce the intended behaviour (probably not helped by the name of the function). The intended behaviour is to copy the values from the static instance into the this pointer, not initialise the static instance, which is what your change does.

Hopefully that makes sense

Tom

SergeyKostrov
Valued Contributor II
129 Views

Hi Tom,

It is clear that something is wrong. Here are a couple of questions:

- Could you re-post sources for'IntelTestCase' in a zip-file?

- Did you try to verify your 'IntelTestCase' with a different C/C++ compiler, forexample MS C/C++ compilerwith any Visual Studio?

I have two C/C++template-projects ( not C++ templates! ) for a Win32application with a Win32 DLL and
for a Win32application with anMFC DLL. One of them has an exported class and it works.

So, let me know if you're interested to look at these two small projects.

Note: They don't do initialization in the way you do, but who knows, maybe it could help to understand what is wrong with your 'IntelTestCase'.

Here is an example howI declared an exportedclass, a global variable, and a C function:

///////////////////////////////////////////////////////////////////////////////
// TmplMfcDllTest.h

#pragma once

///////////////////////////////////////////////////////////////////////////////

#ifdef TMPLMFCDLL_EXPORTS
#define TMPLMFCDLLAPI __declspec( dllexport )
#else
#define TMPLMFCDLLAPI __declspec( dllimport )
#endif

///////////////////////////////////////////////////////////////////////////////

#if defined ( _WIN32_MSC )
#include "Resource.h"
#endif
#if defined ( _WIN32CE_MSC )
#ifdef POCKETPC2003_UI_MODEL
#include "ResourcePpc.h"
#endif
#ifdef SMARTPHONE2003_UI_MODEL
#include "ResourceSp.h"
#endif
#endif

///////////////////////////////////////////////////////////////////////////////
// Exported Class

class TMPLMFCDLLAPI CTmplMfcDll
{
public:
CTmplMfcDll();
virtual ~CTmplMfcDll();

public:
RTvoid TestMethod();
};

///////////////////////////////////////////////////////////////////////////////
// Exported Global Variable

extern TMPLMFCDLLAPI RTint g_iTmplMfcDll;

///////////////////////////////////////////////////////////////////////////////
// Exported CFunction

RTint TMPLMFCDLLAPI FnTmplMfcDll( RTvoid );

///////////////////////////////////////////////////////////////////////////////

dan5
Beginner
129 Views

This problem only happens with the Intel compiler, compiling with the VS compiler and same optimization options produces seemingly valid asm. Have reattached the solution as a zip file for anyone who could shed any more light on the problem.

Otherwise I've got a response from Intel to say they've reproduced the case and are currently looking at the issue. Will post a reply if I hear anything else.

Using any of the workarounds I posted in my original post will get round the problem.

Tom
SergeyKostrov
Valued Contributor II
129 Views

Here are results of my review:

'IntelTestCaseMain' has two base configurations:

Win32
x64

'IntelTestLoaderDLL' has one base configuration:

x64

'IntelTestPluginDLL' has one base configuration:

x64

Is there any possibility that you called 64-bit DLLs from the Win32 application?

Like:

'IntelTestCaseMain 32-bit' -> 'IntelTestLoaderDLL 64-bit' -> 'IntelTestPluginDLL 64-bit'

In general, I don't think that it will ever work. It is similar to a very old problem of calling a 32-bit DLL
from a 16-bit Application, or calling a 16-bit DLL from a 32-bit Application.

It can't be done directly. Please take a look at MSDN and search with a key-word 'Thunk'.

Cases like:

'IntelTestCaseMain 32-bit' -> 'IntelTestLoaderDLL 32-bit' -> 'IntelTestPluginDLL 32-bit'

or

'IntelTestCaseMain 64-bit' -> 'IntelTestLoaderDLL 64-bit' -> 'IntelTestPluginDLL 64-bit'

should work.

Best regards,
Sergey

PS:
On one of my project a problem of mixing 16-bit and 32-bit modules was solved by using
a Middle-Level DLL, called as a GateDLL, and a Dynamic Data Exchange ( DDE ) Interface.

Here is a scheme how it worked:

Passing some data to a 16-bit DLL from a Win32 application:

'MainApp 32-bit DDE client' -> DDE -> 'GateDll 16-bit DDE server' -> 'SomeDLL 16-bit'

and
Passing some data from a 16-bit DLL to a Win32 application:

'SomeDLL 16-bit' -> 'GateDll 16-bit DDE client' -> DDE -> 'MainApp 32-bit DDE server'

It is a classic example of a Client-Server architecture.

Brandon_H_Intel
Employee
129 Views

Hi Tom,

Intel C++ Composer XE 2011 update 9 should now resolve this issue with the access violation. Let us know if you need anything else.
Reply