The VC++ compiler generally maintains a high degree of binary compatibility between compiler versions, allowing objects to be passed between DLLs made with different compiler versions. However VS 2013 made changes to the layouts of some classes in 64-bit builds.

Luckily this only affects a tiny percentage of classes, and if you do hit this problem there is a simple solution. This simple solution can also be used to reduce the size of classes in both 32-bit and 64-bit builds.

The layout change happens if you have a class that has virtual functions, does not derive from a class with virtual functions, contains a member that needs 16-byte alignment or higher which isn’t the first data member, and you are compiling for 64-bit.

Clarification: the VC++ compiler maintains a high-level of binary compatibility to allow DLLs built with one version to coexist with DLLs built with another version. The C++ run-time and STL do not maintain binary compatibility between versions.

This class layout change was notable to me because I could not think of another time when a VC++ upgrade has changed the layout of a class (other than through the class definition changing, which is what happens with the CRT and STL). If you know of a case where the layout has changed, let me know in the comments.

What VS 2010 does

To make this discussion concrete, here’s a class definition:

class VirtualVecOne

{

public:

virtual ~VirtualVecOne() {}

void* p;

__m128 v;

};

This class will consist of a v-table pointer, the pointer member variable p, and the vector member variable v. VC++ aligns built-in types in structs and classes to a multiple of their size, but if you squeeze the items in this class together then in 64-bit builds they automatically end up naturally aligned, so it is possible to lay out VirtualVecOne like this:

And with 64-bit builds in VC++ 2010 and VC++ 2012 this is exactly how this class is laid out. Short, sweet, no padding. But it appears that this was a bug.

What is supposed to happen (in VC++)

For historical reasons the VC++ rule for laying out these classes is that the first member of the class after the v-table pointer should be aligned at the highest alignment requirement of any of the class members. That is, if the class has an __m128 member that requires 16-byte alignment then the first member is supposed to* be 16-byte aligned. That means the compiler inserts padding bytes after the v-table, and perhaps elsewhere in the class. That’s not ideal, but it’s been the VC++ layout rule for decades, and this layout is what VC++ 2013 does.

* By ‘supposed to’ I mean by VC++ rules not by C++ standard rules. The standard leaves padding of struct/class layouts to the implementation.

In other words, according to the VC++ rules, the 64-bit compiler is supposed to lay out the class like this:

Note the 16 bytes of padding in two blocks of 8. With VC++ 2013 the 64-bit compiler changed from Layout A to Layout B. The offsets of p and v and the size of the class all changed. I discovered this because one class (out of thousands in the ~600 projects I ported) was laid out similarly to VirtualVecOne. When we passed it from a VC++ 2010 DLL to a VC++ 2013 DLL, things broke.

For completeness, here’s what the class looks like in the 32-bit compiler, all versions, with 24 bytes of padding:

Workarounds

Layout B may be the VC++ standard, but it is bloated, and it is incompatible with VC++ 2010, and both of these are potential problems. What I wanted was a way to get VC++ 2013 to use the old layout style. And I found the answer because of a tweet that was pseudo-randomly sent to me a few days earlier. That tweet linked to a bug which talked about layout errors in the VC++ x64 compiler. That bug linked to a page which explained the layout rules, and showed how to change them. Pretty cool. My superpower is learning things just-in-time and it worked perfectly this time.

The workaround is to inherit from a class that has virtual functions. An otherwise empty class with a virtual destructor is the ideal choice. So I changed the class to look like this:

class LayoutFixer

{

public:

virtual ~LayoutFixer() {}

}; class VirtualVecOne : public LayoutFixer

{

public:

virtual ~VirtualVecOne() {}

void* p;

__m128 v;

};

That’s it. Problem fixed. The class is now laid out efficiently (Layout A) and the layout is now consistent between VC++ 2010, 2012, and 2013. The structure will also get smaller in 32-bit builds. Drink some Scotch and go home early.

Note that while the compatibility aspect of this problem doesn’t happen with the 32-bit compiler, the wasted space issue does. So the empty base class technique is valid for 32-bit programs and can save space in classes with virtual functions and double, __int64, or __m128 members.

Diagnostics

There are a few things to know when investigating this sort of thing. One is to watch the debugger. I realized what was happening when I stepped from one function to another and saw that the object whose address I passed across the boundary went from valid to invalid. I could go up and down the stack and see that this object – with its address unchanging – would be displayed correctly and then incorrectly, depending on which DLL contained the active function on the stack. It was pretty cool actually. The lesson there is that debug information is associated with a particular source file and may vary within a debug session.

In the world of Windows it is actually entirely legal to have the layout of a class change between DLLs. The one-definition-rule applies on a per-DLL basis. This means that I can have a class called Vector and you can have a class called Vector and our DLLs can coexist in one process. If we pass a Vector from one DLL to another then their definitions had better match, but it is our job to ensure that!

You can get VC++ to dump the layout of a particular class or all classes by using the undocumented /d1reportSingleClassLayoutVirtualVecOne or /d1reportAllClassLayout flags. The output is a bit weird – it doesn’t reliably indicate padding – but it can still be quite useful. Here’s the VC++ 2013 result with LayoutFixer – it’s similar to the results you would get with VC++ 2010:

class VirtualVecOne size(32):

+—

| +— (base class LayoutFixer)

0 | | {vfptr}

| +—

8 | p

16 | __m128 v

+—

Here’s the VC++ 2013 result without LayoutFixer – note that it explicitly indicates the padding after ‘p’ but not the padding before:

class VirtualVecOne size(48):

+—

0 | {vfptr}

16 | p

| <alignment member> (size=8)

32 | __m128 v

+—



Finally, when investigating class layout issues you can always use offsetof together with printf or static_assert, like this:

printf(“Offset of VirtualVecOne::p is %u

”, (unsigned)offsetof(VirtualVecOne, p));

printf(“Offset of VirtualVecOne::v is %u

”, (unsigned)offsetof(VirtualVecOne, v));

printf(“sizeof(VirtualVecOne is %u

”, (unsigned)sizeof(VirtualVecOne));

Why has nobody else reported this compatibility problem?

Most people haven’t upgraded to VC++ 2013 yet

It’s 64-bit only

It only happens if you mix VC++ 2010/2012 with VC++ 2013 binaries

Many classes don’t have virtual functions.

Most classes don’t contain __m128 members.

Those that have virtual functions often inherit from classes that have virtual functions (interfaces) which avoids the bug.

Remember that the space wastage issue is much more common than the compatibility problem, so look for that in your 32-bit and 64-bit types if you create a lot of them.

Related notes

I wrote a while ago about the poorly understood behaviors of bitfields in structures.

Extra credit question

What are 64-bit integer variables aligned to? Answer for as many x86/x64 compilers as possible. I only learned this earlier this year.

Talkback

Some people on reddit disagreed with my claim that the VC++ compiler generally maintains binary compatibility. One commenter found documentation of a structure layout change between VC++ 4.2 and 5.0. So that’s two in about twenty years, one of which is documented. That, plus my discussions with VC++ developers, leaves me feeling okay about my claim.

Another reader (I love my readers – I learn a lot from writing this blog) found some VC++ 2013 documentation that explains this breaking change and how to detect it. Pretty cool. Go to http://msdn.microsoft.com/en-us/library/bb531344.aspx and search for “Object layout has changed”. Note in particular that warning C4370 with VS 2010 can be used to detect this problem.