本征静态库 aligned_free "double free or corruption"

Eigen static lib aligned_free "double free or corruption"

这是 earlier post 的延续。

但这次有一个希望更好的例子。这个简单的测试在设置向量时崩溃了。

我正在使用 Ubuntu 20.04、gcc 9.3.0、c++17、eigen 3.3.7

main.cpp

#include <iostream>
#include <memory>
#include "C.h"

using namespace std;

class B { 
public:
    C c;

    B() {
        cout << (uint64_t)this << endl;
    }
    ~B(){}

};

int main() {

    shared_ptr<B> b = make_shared<B>();

    b->c.v = VectorXd(5); // << crashes here
    // SIGABRT on `Eigen::internal::aligned_free` when trying to do `std::free()`

    return 0; 
}

Class C 作为静态库链接。静态库在 Release 模式下编译 (--std=c++17 -DNDEBUG -O3),而主程序在 Debug 模式下 运行 - 没有优化。 如果Debug中静态库和主程序都是运行,那么就没有crash。 此外,如果它们在 Release 中都是 运行,则不会发生崩溃。只有在Release编译静态库,Debug编译主程序时才会出现此问题

C.h

#pragma once
#include <vector>
#include <Eigen/Core>

using namespace std;
using namespace Eigen;

class C {
public:

    VectorXd v;

    C();
    ~C();
};

C.cpp

#include "C.h"

C::C() {
    v = VectorXd(5);
}

C::~C() {

}

如果您使用不匹配的体系结构标志 (-m... flags for gcc) 编译库和主要可执行文件,您可以观察到崩溃。例如,当我编译启用了 avx (-mavx) 的库和没有启用 avx 的主要可执行文件时,反之亦然,它崩溃了。崩溃也发生在 Eigen 3.4.0 中。此外,它也会在没有优化的情况下发生 (-O0).

Eigen aligned_malloc()aligned_free() 函数根据 EIGEN_DEFAULT_ALIGN_BYTES 宏的值做不同的事情,例如当AVX(以及其他)被启用或禁用。 Eigen 根据使用的编译器标志自动选择合适的值。因此,如果仅在静态库中启用 AVX,C 的构造函数在内部使用 handmade_aligned_malloc(),它不会直接返回 malloc() 接收到的指针,而是将其移动一点以确保正确对齐。另一方面,从可执行文件(在没有 AVX 的情况下编译)中为 C::v 分配一个新值将首先尝试通过调用 b->c.v.~VectorXd() 来释放内存,然后最终只调用 free() 而不是 handmade_aligned_free()。因此,free() 接收到的地址不指向已分配内存的开头,从而导致崩溃。 当然,如果您在可执行文件中启用 AVX 而在库中禁用它,则会得到对 malloc()handmade_aligned_free() 的不匹配调用,也会导致崩溃。

解决方案很简单:确保使用相同的体系结构标志编译静态库和可执行文件。如果出于某种原因这是不可能的,请确保创建和销毁始终发生在同一组件中。例如,您可以将 C::v 设为私有并仅允许通过 C.cpp 文件中实现的函数对其进行修改。

注意:您的 other post 有点不同,因为它不涉及动态内存管理,而目前 post 您使用的 VectorXd 确实使用动态内存管理。

注2:描述的问题与std::shared_ptr的使用无关。但是对于 3.4.0 之前的 Eigen 版本,您可能需要使用 EIGEN_MAKE_ALIGNED_OPERATOR_NEW 来确保指针 b 正确对齐,而不管体系结构和 C++17;至少最初的 Eigen issue 只为 3.4.0 实现。但我可能错了。