Pointers to members break compilation in a complicated x64 project

Pointers to members break compilation in a complicated x64 project

My project is crashing if it is compiled for x64 using ICL12. MSVC2010 works fine.
I think it is crashing because ICL12 treat pointers to members (like int T::*val;) as 32-bit in some special cases.
Of course it should be 64-bit.
I tried very hard to write a simple sample code to reproduce this, but in vain.
It is treated as 64-bit as it should. I cannot find what is really breaking compilation.
The struct that has the problematic pointer is defined in another class, and stored in hash_map...

If you are interested in this problem, please compile and test our project.
Our project is a open source software, "ffdshow".

After compilation, I'm sure you can find where it is crashing soon.
Open a DirectShow compatible player and open some file, the player crashes immediately.
I'm afraid it is very complicated to read. I hope you are patient enough.

How to compile our project is documented here.

P.S. Thank you very much for your help to our project. Intel QuickSync Decoder by Eric Gur is great.

22 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

The attached image is a screen shot of the debugger.
It is crashing in ntdll.dll and I traced back in Toption::TintOption::reg_op.
Toption::TintOption is derived from TintOptionT.
Memory dump at this pointer looks correct.
But "watch" of this is not. After val, which is a pointer to a member, all values are read from -4 byte offseted place.

Quoting h_yamagataMy project is crashing if it is compiled for x64 using ICL12. MSVC2010 works fine.
I think it is crashing because ICL12 treat pointers to members (like int T::*val;) as 32-bit in some special cases.
...

What do you mean a "special case"?

The 'int' type is a built-in type and the IntelliSenseclearly shows it in the"blue" color. This is what I see
in a screenshot enclosed for'Toptions.h' header on the right part.

Since'int' is the built-in type it is hard to believe that for the same 64-bitproject a C/C++ compiler
( any, not just ICL12 ! )will"treat"'int'in some cases as 64-bit type and in another cases as 32-bit type.

A very simple verification, or verifications,could be done as follows:
...
int n = sizeof( int );
...
in order to confirm that 'n'equals to 8 in your64-bit application.

Best regards,
Sergey

Quoting h_yamagata...ICL12 treat pointers to members (like int T::*val;) as 32-bit in some special cases...
Of course it should be 64-bit.
...

Here is a screenshotof your screenshot:

As you can see the 'val' member is a64-bit type, but it has a very strange value! It is the60th byte from
the 0x0000000000000000 ( beginning of memory ).

For example, a member 'name' has a valid value for a memory address and some valid data (characters ).
I could also guess that a memory for the member 'name' was allocated from the heap.

Best regards,
Sergey

Thank you for reply.

> Since'int' is the built-in type it is hard to believe that for the same 64-bitproject a C/C++ compiler
> ( any, not just ICL12 ! )will"treat"'int'in some cases as 64-bit type and in another cases as 32-bit type.

No, it's not int. It's a pointer to a member. Apart from good or evil, it gives a offset in the class. So the value "0x000000000000003c" is reasonable.

The value is initialized like this.

static const TintOptionT iopts[]= {
        IDFF_isResize           ,&TresizeAspectSettings::is             ,0,0,_l(""),1,
        _l("isResize"),0,
        IDFF_showResize         ,&TresizeAspectSettings::show           ,0,0,_l(""),1,
        _l("showResize"),1,

>What do you mean a "special case"?

I don't know. And that's why I'm showing the link to whole source code.
Because I believe this is a bug, I'm asking Intel to investigate what is special with my case.

Please read the memory dump and the "watch" carefully. "min" which should be 64, is zero because it is read from the part of "val". "max" which should be 0x4000, is 64 because it is read from "min".

>For example, a member 'name' has a valid value for a memory address and some valid data (characters ).
>I could also guess that a memory for the member 'name' was allocated from the heap.

"name" has a invalid value. It should be "0x000007feeacc6854" as I can see from the dump.

Quoting h_yamagata...
No, it's not int. It's a pointer to a member.
...

Let's clarify it. For example,in a structure 'X' must be three members A, B and C. They must be declared
somehow and they must be of some type. Do you agree with it? Now, a declaration of that structure 'X' could
look like:

typedef struct tagX
{
int A; // type int
int *B; // a pointerof type int ( for example, foran array of numbers )
int **C; // a pointer-to-pointer of type int ( for example, fora matrix )
} X;

You can't declare A, B and C without specifying of some type becausea C/C++ compiler won't be able to
compile it. How could I describe themember 'B'?

This is a pointer of type 'int' of the structure 'X'

Now, in your case the member 'val' is a pointer of type 'int' of the structure 'TintOptionT' ( template based ):
...
val 0x000000000000003c int *
...
Please take a look at your screenshot.

Best regards,
Sergey

Quoting h_yamagata...
No, it's not int. It's a pointer to a member.
...

It is a pointer to int, right?

Best regards,
Sergey

>It is a pointer to int, right?

No, definitely not. It's an offset.
Did you read the link I gave you?
Please read
http://publib.boulder.ibm.com/infocenter/comphelp/v7v91/index.jsp?topic=%2Fcom.ibm.vacpp7a.doc%2Flanguage%2Fref%2Fclrc13cplr034.htm
or a good language reference.

I can show you a small example (a x64 console application by ICL12 or MSVC2010).

#include "stdafx.h"

struct Test {
    int a;  // +0
    int b;  // +4
    char c; // +8
    char d; // +9
};

template struct TintOptionT {
    int T::*offset_b;
    char T::*offset_d;
    void print() const
    {
        printf("sizeof(offset_b) = %d, offset_b = %d, offset_d = %d",sizeof(offset_b),offset_b,offset_d);
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    static const TintOptionT iopt = {&Test::b, &Test::d};
    iopt.print();
	return 0;
}

The output is "sizeof(offset_b) = 4, offset_b = 4, offset_d = 9". Both ICL12 and MSVC2010 treat the type as 32-bit.
I think there are some confusion in the size (32 vs 64-bit) of the "pointer to members".
#include "stdafx.h"
#include

struct Test {
int a,b;
};

template struct TintOptionT {
int id;
int T::*val;
int min,max;
void print() const
{
printf("id %d val %I64d min %d max %d\n",id, val, min, max);
}
};

int _tmain(int argc, _TCHAR* argv[])
{
static const TintOptionT iopt = {10, &Test::b, 11, 12};
stdext::hash_map> map;
map[20] = iopt;
const TintOptionT &iopt2 = (*(map.find(20))).second;
iopt2.print();
return 0;
}

>>...I can show you a small example (a x64 console application by ICL12 or MSVC2010)...

...
structTest{

inta;//+0
intb;//+4<= [SergeyK] On a x64 platform for a x64 console applicationcould you havean offset'+4' for a builtin type 'int'?
...

Could you also verify the sizes for 'int', '__int32' and '__int64' types?

...
int iInt = sizeof( int );
int iInt32 = sizeof( __int32 );
int iInt64 = sizeof( __int64 );
...

Best regards,
Sergey

Quoting h_yamagataMy project is crashing if it is compiled for x64 using ICL12. MSVC2010 works fine.
I think it is crashing because ICL12 treat pointers to members (like int T::*val;) as 32-bit in some special cases.
Of course it should be 64-bit.
...

This is what I found on MSDN in 'Common Visual C++ 64-bit Migration Issues' topic:

When you use Visual C++ to create applications to run on a 64-bit Windows operating system, you
should be aware of the following issues:

An int and a long are 32-bit values on 64-bit Windows operating systems. For programs that you plan to
compile for 64-bit platforms, you should be careful not to assign pointers to 32-bit variables. Pointers are
64-bit on 64-bit platforms, and you will truncate the pointer value if you assign it to a 32-bit variable.
...

Could you verifyin your application a sizeof( int )for both cases, that is, when you compile with VS2010 and ICL12?

Quoting h_yamagata>It is a pointer to int, right?

No, definitely not. It's an offset.

#include "stdafx.h"
#include

struct Test {
int a,b;
};

template struct TintOptionT {
int id;
int T::*val;
int min,max;
void print() const
{
printf("id %d val %I64d min %d max %d\n",id, val, min, max);
}
};

int _tmain(int argc, _TCHAR* argv[])
{
static const TintOptionT iopt = {10, &Test::b, 11, 12};
stdext::hash_map> map;
map[20] = iopt;
const TintOptionT &iopt2 = (*(map.find(20))).second;
iopt2.print();
return 0;
}
[SergeyK] What type doesa member that holdsan offsethave?
It must have some type, correct? It can't be declaredwithout some C languagetype.

I'm not asking you about some application specific things. My question isa C language specific and
it isabout how some member of some struct was declared.

Best regards,
Sergey

Quoting h_yamagata...I think there are some confusion in the size (32 vs 64-bit) of the "pointer to members"...
#include "stdafx.h"
#include

struct Test {
int a,b;
};

template struct TintOptionT {
int id;
int T::*val;
int min,max;
void print() const
{
printf("id %d val %I64d min %d max %d\n",id, val, min, max);
}
};

int _tmain(int argc, _TCHAR* argv[])
{
static const TintOptionT iopt = {10, &Test::b, 11, 12};
stdext::hash_map> map;
map[20] = iopt;
const TintOptionT &iopt2 = (*(map.find(20))).second;
iopt2.print();
return 0;
}

Here is a modified Test-Case with numbers fora 32-bit platform and different C/C++ compilers.
I used two additional C/C++ compilers ( MinGW v3.4.2 and Borland C++ v5.5.1 ) to investigate the problem.

Could you test it on 32-bit and 64-bit platforms with MSC and ICL C/C++ compilers?

Note: Don't forget to "synchronize" the codes for theTest-Cases. When the Test-Case 1 is uncommented all the rest are commented, etc.

struct Test
{
// Test-Case 1
int a; // +0
int b; // +4
char c; // +8
char d; // +9

// Test-Case 2
// int a; // +0
// __int32 b; // +4
// char c; // +8
// char d; // +9

// Test-Case 3
// int a; // +0
// __int64 b; // +8
// char c; // +16
// char d; // +17
};

template< class T > struct TintOptionT
{
int T::*offset_b; // Test-Case 1
// __int32 T::*offset_b; // Test-Case 2
// __int64 T::*offset_b; // Test-Case 3
char T::*offset_d;

void print() const
{
printf( "sizeof(offset_b) = %ld, offset_b = %ld, offset_d = %ld\\n", sizeof(offset_b), offset_b, offset_d );
};
};

Outputs are as follows:

// Test-Case 1
MSC sizeof(offset_b) = 4, offset_b = 4, offset_d = 9
MinGW sizeof(offset_b) = 4, offset_b = 4, offset_d = 9
BCC sizeof(offset_b) = 8, offset_b = 5, offset_d = 0

// Test-Case 2
MSC sizeof(offset_b) = 4, offset_b = 4, offset_d = 9
MinGW sizeof(offset_b) = 4, offset_b = 4, offset_d = 9
BCC sizeof(offset_b) = 8, offset_b = 5, offset_d = 0

// Test-Case 3
MSC sizeof(offset_b) = 4, offset_b = 8, offset_d = 17
MinGW sizeof(offset_b) = 4, offset_b = 8, offset_d = 17
BCC sizeof(offset_b) = 8, offset_b = 5, offset_d = 0

Note: Borland C++ compiler changes order of members! That's really interesting because in that case there no a binary compatibility.

> Could you also verify the sizes for 'int', '__int32' and '__int64' types?

I'm talking (or I would like to talk) to a maker of C++ compiler.
We all know the size of int on x64 is 32-bit.
Why do I have to...?

>I'm not asking you about some application specific things.

It's not application specific at all.
Please consult a good language reference of C++ and understand correctly.
The sample code I pasted helps a lot.

>[SergeyK] What type doesa member that holdsan offsethave?
> It must have some type, correct? It can't be declaredwithout some C languagetype.

intT::*
T is a class or struct. intT::* is completely different from int * or int **.
And it's C++, not C.

>What do you mean a "special case"?

I have a progress on this.
I'll explain later. Please wait.

I finally discovered what was breaking my project.
I have succeeded in writing a short sample code that represents the problem our project.

About the code below,

The x64 binary crashes if it is compiled by ICL12. MSVC2010 works.
There is an evil cast (int TestBase::*). If the cast is (int Test::*), it works.
If cast to (int TestBase::*) is used, iopts.offset_b is initialized using 64-bit data. And so the lower half of "str" is written by it. The higher half of "str" is initialized by the lower half of the pointer to "test string". As a result, it ends up crashing in print().

Please don't fall into an off topic discussion that the cast is too evil or wrong. I know the cast is wrong in a sense that it points to wrong place, if it is used for the base class or other derived classes. We have been using the cast just because it is convenient. It is convenient for a reason. The reason? It's too much off topic.

#include "stdafx.h"

struct TestBase {
};

struct Test :public TestBase {
    int a;  // +0
    unsigned int b;  // +4
    char c; // +8
    char d; // +9
};

template struct TintOptionT {
    int T::*offset_b;
    char *str;
    void print() const
    {
        printf("sizeof(offset_b) = %d, offset_b = %d, str = %s", 
                sizeof(offset_b),      offset_b,      str);
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    static const TintOptionT iopts = {(int TestBase::*)&Test::b, "test string"};   // crashes
//  static const TintOptionT iopts = {(int Test::*)&Test::b, "test string"};       // works
    iopts.print();
	return 0;
}

Hi,
Could you please let me know which version of icl compiler you are using. This can be found in VS2010 (Tools-->Options-->Intel c++--> Compiler Information).

Because i am not able to reproduce the crash that you have described in the testcase and both the declarations work fine without any crash.

Thanks & Regards,
Sukruth H.V

Hello,

yes that's indeed a bug in the compiler. Thank you very much for providing this simple reproducer. I've assigned a defect ticket to our compiler engineers (DPD200272597).

It seems to me that pointers to members cannot be correctly assigned to signed integers. Another "workaround" might be the following for you:

#include 
#include 
  
struct TestBase {  
};  
  
struct Test :public TestBase {  
    int a;  // +0  
    unsigned int b;  // +4  
    char c; // +8  
    char d; // +9  
};  
  
template struct TintOptionT {  
    unsigned int T::*offset_b;  
    char *str;  
    void print() const  
    {  
        printf("sizeof(offset_b) = %d, offset_b = %d, str = %s",   
                sizeof(offset_b),      offset_b,      str);  
    }  
};  
  
int _tmain(int argc, _TCHAR* argv[])  
{  
    static const TintOptionT iopts = {(unsigned int TestBase::*)&Test::b, "test string"};		       // workaround
//  static const TintOptionT iopts = {(int Test::*)&Test::b, "test string"};					       // works  
//  static const TintOptionT iopts = {static_cast(&Test::b), "test string"}; // crashes  
    iopts.print();  
    return 0;  
} 

Assigning pointers to unsigned integers works and would fit much better to your existing semantic.

Edit: I'll let you know for which version this will be fixed.

Best regards,

Georg Zitzlsberger

> Could you please let me know which version of icl compiler you
are using. This can be found in VS2010 >(Tools-->Options-->Intel
c++--> Compiler Information).

Intel C++ Compiler on IA-32, version 12.1.2 Package ID: composer_2011_update8.079

>yes that's indeed a bug in the compiler. Thank you very much for
providing this simple reproducer.
>I've assigned a defect ticket to our
compiler engineers (DPD200272597).

Thank you.

>> ICL12 treat pointers to members (like int T::*val;) as 32-bit in some special cases

Your T::*val is not a "pointer to member" per say, rather it is "a member that is a pointer to a T", and in the shown case T==int. This pointer will be the size of pointer on your system.

*** FastDelagate.h ***

The paragraph above applies if you are managing your own data types.

In FastDelagate though, you may be attempting to declare a union or data overlay that is overlaid on top of an internal structure used by the code generated by the compiler to access vtables or derived member variables. In these situations, the internal values used to access these tables is vendor specific, they may be pointers or offsets. It is your responsibility to adaptyour code to the internal tables. The authors of FastDelagate did an excellent job of sorting this out, and it looks as if you are trying to use your own code as opposed to a provided component in FastDelagate. I suggest you research the routines/templates in FastDelagate to see if one fits your needs.

Jim Dempsey

www.quickthreadprogramming.com

>Your T::*val is not a "pointer to member" per say, rather it is "a
member that is a pointer to a T",
>and in the shown case T==int. This
pointer will be the size of pointer on your system.

No, it's "int T::*val". It's not "T::*val". T is TresizeAspectSettings or other derived class from Toptions. The size of "int T::*" is 32-bit as explained in this thread.

>The authors of FastDelagate did an excellent job of sorting this out,
and it looks as if you are trying to
>use your own code as opposed to a
provided component in FastDelagate. I suggest you research the

>routines/templates in FastDelagate to see if one fits your needs.

The internal code of FastDelagate.h is too difficult for me. But it's working. I have modified my project to avoid evil cast to "pointer to members of base class", and it's working now. Thanks.

Best Reply

Hello,

this bug has been fixed and will be available with Intel Composer XE 2011 Update 10.

Best regards,

Georg Zitzlsberger

Thank you very much.

Leave a Comment

Please sign in to add a comment. Not a member? Join today