Reputation: 4884
I am using C++17, GCC 7.4.0, Eigen 3.3.4
This is my minimal example. I have 2 classes: B
and C
. C
is in a static library.
The program crashes with Segmentation Fault when trying to create an instance of B
.
Static library is built with optimizations (Build Type: Release). If built without optimizations, the program does not crash. The program only crashes when it's run in Debug. If I run it in Release, it does not crash.
main.cpp
#include "B.h"
using namespace std;
int main() {
B b; // Program crashes here
return 0;
}
B.h
#pragma once
#include "C.h"
using namespace std;
class B {
public:
C c;
B();
~B();
};
B.cpp
#include "B.h"
#include <iostream>
B::B() {
cout << (uint64_t)this << endl; // This is never printed. Program crashes before this gets printed
}
B::~B() {
}
C.h
#pragma once
#include <vector>
#include <eigen3/Eigen/Core>
#include <eigen3/Eigen/Geometry>
using namespace std;
using namespace Eigen;
class C {
public:
Quaterniond q;
Vector3d v;
C();
~C();
};
C.cpp -- this is packed into a static library
#include "C.h"
C::C() {
v = Matrix<double, 3, 1>{0, 0, 0};
Eigen::AngleAxisd y90(M_PI / 2, Eigen::Vector3d::UnitY());
q = Quaterniond(y90);
}
C::~C() {
}
Also, if I remove Quaterniond q
from the class C
, re-build the static lib, and run the program, it does not crash.
EDIT:
This looks to me connected to Eigen memory alignment issues. But, as it says in the link, I should not experience these issues since I am using C++17.
Anyways, according to @mmomtchev suggestion, I modified my code like this:
C.h -- modified
#pragma once
#include <vector>
#include <eigen3/Eigen/Core>
#include <eigen3/Eigen/Geometry>
using namespace std;
using namespace Eigen;
class C {
public:
Matrix<double, 3, 1, Eigen::DontAlign> v;
Quaternion<double, Eigen::DontAlign> q;
C();
~C();
};
And the program doesn't crash. So this supports my theory... It could be simply that one is not safe with C++17 if using static libs.
EDIT 2:
I posted a continuation here. I wasn't sure whether this is the same or a different problem.
Upvotes: 4
Views: 993
Reputation: 6142
Judging from your comments at your other post, you might be using different architecture options while compiling the library and the executable (-m...
flags for gcc)?
At least, I could reproduce the crash using Eigen 3.3.4 when I compiled the static library in gcc-7.5.0 with the flags -O0 -DNDEBUG -g -std=c++17 -mavx
, while the executable is built with -O0 -DNDEBUG -g -std=c++17
. This results in a segmentation fault in _mm256_store_pd()
, which gets called when Eigen tries to assign a new value to C::q
in the line q = Quaterniond(y90);
in C::C()
.
The crash is not completely deterministic.
The crash has the following reason: Object B
and thus also its member c
is created on the stack in the main executable. Its alignment is enforced by Eigen to a certain value. See the specialization of plain_array
in Eigen. That value depends on the active architecture. Without any special flags it is 16 bytes, so the address of the local variable b
is e.g. 0x7fffffffe670
, and thus also of B::c
, and thus also of C::q
(all of them are the first members).
Then the code assign a new value to C::q
from within the static library which was compiled with AVX in my example. Thus, it uses the AVX instruction _mm256_store_pd()
for this, which requires 32 byte alignment. But the address 0x7fffffffe670
is only 16 byte aligned, not 32. Therefore, it causes a segmentation fault.
For me, sometimes the local variable happens to be 32 byte aligned, sometimes not. No idea what this depends on.
The macro EIGEN_MAKE_ALIGNED_OPERATOR_NEW
does not help here, because no dynamic memory allocation is involved. In fact, it wouldn't help anyway: In your other post the code involves dynamic memory allocations, but as explained there, you once again run into mismatching assumptions of alignment when the architectures are different.
The optimization level should have nothing to do with the problem, and AFAIK gcc does not automatically use different target architectures at different optimization levels. It also should have nothing to do with C++17 specifically as Eigen is using compiler specific attributes to align the data on the stack correctly.
The solution is to ensure that you compile the static library and the executable with the same architecture flags.
Upvotes: 4