[cfe-dev] C++11 ABI Compatibility and Static Initialization

Kevin Locke kevin at kevinlocke.name
Tue Apr 8 22:43:26 PDT 2014


On Tue, 2014-04-08 at 18:50 -0400, Erik Schnetter wrote:
> On Apr 7, 2014, at 18:11 , Kevin Locke <kevin at kevinlocke.name> wrote:
>> Now that I have identified that the issue is C++11 ABI compatibility,
>> I can easily avoid the issue on my systems.  However, I have brought
>> it to your attention in the hopes that it can either made easier to
>> identify or the compatibility can be improved to save future users the
>> effort spent debugging to identify the same issue.
> 
> From my reading of the Boost sources, it seems that Boost determines
> at compile time whether constexpr is available, and uses it if so.
> This means that Boost consists of different C++ code depending on
> whether -std=c++11 is used or not. I would assume that this means
> that the resulting object code should not be expected to work.
> 
> My initial reaction is that this is an error in Boost (which could
> make this distinction at configuration time instead of at compile
> time), or that Boost should at least detect this problem and abort
> with an error. Of course, if clang could detect this
> incompatibility, this would also be good.

Hi Erik and Mehdi,

Thanks for considering the issue!  You are quite right about the
conditional use of constexpr and Boost's decision not to pursue binary
compatibility across C++ dialects.  A bug report to boost to make the
error more easily identifiable may be warranted, depending on your
thoughts on the root of the issue, discussed below.

I wonder if Boost may have been a bit of a red herring as to whether
there is a clang issue (or potential improvement) for this case.  So
I've worked out a more minimal example which does not rely on Boost:

-8<--libawesome.hpp---------------------------------------------------
#if __has_feature(cxx_constexpr)
#define CONSTEXPR_OR_CONST constexpr
#else
#define CONSTEXPR_OR_CONST const
#endif

inline double dummy(const double* num) {
    return 1.0L;
}

inline CONSTEXPR_OR_CONST double maybe_constexpr(double v)
{
   return v;
}

template <class Awesome>
struct awesome_initializer
{
   struct init
   {
      init()
      {
         Awesome::calc_stuff();
      }
      void force_instantiate()const{}
   };
   static const init initializer;
   static void force_instantiate()
   {
      initializer.force_instantiate();
   }
};
template <class Awesome>
typename awesome_initializer<Awesome>::init const awesome_initializer<Awesome>::initializer;

struct awesome
{
   static double calc_stuff()
   {
      awesome_initializer<awesome>::force_instantiate();
      static const double num[1] = {
         maybe_constexpr(1.0)
      };
      return dummy(num);
   }
};
-8<--libawesome.hpp---------------------------------------------------

-8<--libcrash11.cpp---------------------------------------------------
#include "libawesome.hpp"

double libfunc() {
    return awesome::calc_stuff();
}
-8<--libcrash11.cpp---------------------------------------------------

-8<--crash11.cpp------------------------------------------------------
#include "libawesome.hpp"

int main(int,char**) {
    awesome::calc_stuff();
    return 0;
}
-8<--crash11.cpp------------------------------------------------------

As before, they can be compiled as follows:
clang++ -dynamiclib -o libcrash11.dylib libcrash11.cpp
clang++ -std=c++11 -L. -lcrash11 -o crash11 crash11.cpp

Also as before, executing crash11 will result in an EXC_BAD_ACCESS
error and program crash.

It is worth noting that the error can be caused by a header-only
library shared (possibly as an internal implementation detail) by the
program and a library on which it depends, even when neither the
program nor library use any C++11 features.  Also note that only
fundamental types are passed between the program and the library (in
the example, only the double returned by the library function), making
the error all the more unexpected.

Is there anything which can be done to improve the situation in cases
like this, or is some hairy static initializer debugging just the
price that everyone has to pay for making this sort of mistake?

Thanks for considering once again,
Kevin




More information about the cfe-dev mailing list